BEGIN; -- Replaces the boolean processed/processed_successfully pair with a -- three-state model driven by two nullable timestamps: -- unprocessed: notified_at IS NULL -- notified: notified_at IS NOT NULL AND processed_at IS NULL -- processed: processed_at IS NOT NULL -- The dispatcher sets notified_at each time it hands an event to the -- listener (initially, and again on retry if the listener never acks). -- Only the listener's ack, via its own timestamped write, sets -- processed_at - it's a separate write specifically so it never races the -- transaction that recorded notified_at. ALTER TABLE mock.shop_amazon_events ADD COLUMN notified_at TIMESTAMPTZ, ADD COLUMN processed_at TIMESTAMPTZ; UPDATE mock.shop_amazon_events SET processed_at = NOW() WHERE processed; ALTER TABLE mock.shop_amazon_events DROP COLUMN processed, DROP COLUMN processed_successfully; DROP INDEX IF EXISTS mock.shop_amazon_events_by_timestamp_unprocessed; CREATE INDEX shop_amazon_events_by_timestamp_unprocessed ON mock.shop_amazon_events (shop_id, event_timestamp) WHERE processed_at IS NULL; COMMIT;