-- v1.0.7 item B (day 1): partial composite index for the reversal -- worker's hot path. -- -- The worker's select per tick is: -- SELECT * FROM seller_transfers -- WHERE status = 'reversal_pending' -- AND (next_retry_at IS NULL OR next_retry_at <= NOW()) -- ORDER BY next_retry_at NULLS FIRST -- LIMIT 20; -- -- Migration 116 already provides idx_seller_transfers_retry on -- `(status, next_retry_at) WHERE status='failed' AND next_retry_at -- IS NOT NULL` for TransferRetryWorker. That index cannot serve the -- reversal worker's query because (a) the partial WHERE clause -- excludes reversal_pending rows, and (b) it excludes rows with -- next_retry_at NULL — which are exactly the freshly-inserted -- reversal_pending rows the worker should pick up on first pass. -- -- This migration adds a sibling partial index scoped to -- reversal_pending, including NULL next_retry_at so the first-pass -- rows land in the index. -- -- Why not widen migration 116's existing index instead: widening a -- partial index requires dropping and recreating it, which would -- require a table-level lock. Two parallel partial indexes is the -- cheaper DDL. -- -- No state machine enforcement in this migration — the authoritative -- transition matrix lives in -- `internal/core/marketplace/transfer_transitions.go` and is exercised -- by TestTransferStateTransitions. Day 2 routes call sites through -- the matrix; day 3 exercises the end-to-end flow in a smoke probe. CREATE INDEX IF NOT EXISTS idx_seller_transfers_reversal_pending ON seller_transfers(status, next_retry_at) WHERE status = 'reversal_pending';