perf: add composite indexes for C1/C5, C3, and C2 stale sweep (COW-886)#93
Conversation
- generator_c1c5_poll_idx(chainId, status, allCandidatesKnown, lastCheckBlock): covers C1 and C5 per-block SELECTs which filter on all four columns and ORDER BY lastCheckBlock. Replaces the old partial generator_check_block_active_idx (nextCheckBlock, status) which did not cover chainId or allCandidatesKnown. - discrete_order_c3_status_idx(chainId, status, promotedAt): covers C3 per-block SELECT WHERE chainId + status='open' ORDER BY promotedAt. The previous statusIdx(status) had no chainId and no promotedAt — PostgreSQL would need a separate sort step on every block. - candidate_discrete_order_stale_idx(chainId, validTo): covers C2 stale sweep SELECT WHERE chainId + validTo <= timestamp LIMIT 500. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
lgahdl
left a comment
There was a problem hiding this comment.
Review: COW-886 — Composite indexes
Code Quality ✅
- Index names follow the C1-C5 naming convention established in the codebase
- The comments in each index block correctly document which handler uses them and the exact query shape (equality filters + ORDER BY)
- Replacing the old (which lacked
chainIdand started with a range column) with the new composite indexes is correct index design
Coherence with objective ✅
- All three indexes directly mirror the query patterns in the block handlers
c1c5PollIdxcombines C1 and C5 into one index, which is appropriate since they differ only inallCandidatesKnownc3StatusIdxincludespromotedAtfor theORDER BYadded in COW-988 (that PR adds.orderBy(asc(promotedAt)).limit()to the C3 query — this index is designed to work with it)
Functionality ✅
- All 3 indexes follow the correct composite key order: equality columns first, then the sort/range column last
Minor note (no change needed)
The c1c5PollIdx does not include nextCheckBlock (used as a range filter: nextCheckBlock <= currentBlock). This is intentional and acceptable: C5 uses IS NULL OR nextCheckBlock <= currentBlock which cannot be served by a B-tree range scan anyway. The equality predicates on chainId + status + allCandidatesKnown narrow the working set sufficiently. A follow-up could add nextCheckBlock for C1's non-nullable range, but it's not blocking.
lgahdl
left a comment
There was a problem hiding this comment.
Review: COW-886 — Composite indexes
Code Quality: PASS — Index names follow the C1-C5 naming convention. The comments correctly document which handler uses each index and the exact query shape (equality filters + ORDER BY). Replacing the old single-column checkBlockActiveIdx (which lacked chainId and started with a range column) is correct index design.
Coherence: PASS — All three indexes mirror the actual query patterns in the block handlers. c1c5PollIdx correctly covers both C1 and C5 since they differ only in allCandidatesKnown. c3StatusIdx includes promotedAt for the ORDER BY added in COW-988 (that branch adds .orderBy(asc(promotedAt)).limit() to the C3 query — this index was designed to work with it).
Functionality: PASS — Composite key order is correct: equality columns first, sort column last.
Minor note (no change needed): c1c5PollIdx does not include nextCheckBlock which is used as a range filter. This is acceptable — C5 uses IS NULL OR nextCheckBlock <= currentBlock which cannot be served by a B-tree range scan anyway. The equality predicates on chainId + status + allCandidatesKnown narrow the set sufficiently. Adding nextCheckBlock for C1's non-nullable case is a potential follow-up but not blocking.
|
Superseded by #102 ( |
Summary
Adds three composite indexes covering the hot query paths that run on every block.
generator_c1c5_poll_idx(chainId, status, allCandidatesKnown, lastCheckBlock)conditional_order_generatorORDER BY lastCheckBlockdiscrete_order_c3_status_idx(chainId, status, promotedAt)discrete_orderWHERE chainId + status='open' ORDER BY promotedAtcandidate_discrete_order_stale_idx(chainId, validTo)candidate_discrete_orderWHERE chainId + validTo <= timestamp LIMIT 500The old
generator_check_block_active_idx(nextCheckBlock, status)is dropped — it didn't coverchainIdorallCandidatesKnown, so it was rarely useful for C1/C5. The new index covers those queries end-to-end.Validation
EXPLAIN ANALYZEon production data is recommended after deploy to confirm index scans are being used. The query patterns are deterministic from the code, so the index choices are sound, but row counts and selectivity should be verified.Test plan
pnpm codegen— cleanpnpm typecheck— cleanpnpm test— 66/66 passEXPLAIN ANALYZEon the C1, C3, and C2 stale queries and confirm index scansCloses COW-886
🤖 Generated with Claude Code