Skip to content

perf: add composite indexes for C1/C5, C3, and C2 stale sweep (COW-886)#93

Closed
lgahdl wants to merge 1 commit into
developfrom
luizhatem/cow-886-add-composite-indexes-for-block-handler-polling-queries
Closed

perf: add composite indexes for C1/C5, C3, and C2 stale sweep (COW-886)#93
lgahdl wants to merge 1 commit into
developfrom
luizhatem/cow-886-add-composite-indexes-for-block-handler-polling-queries

Conversation

@lgahdl

@lgahdl lgahdl commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds three composite indexes covering the hot query paths that run on every block.

Index Table Covers
generator_c1c5_poll_idx(chainId, status, allCandidatesKnown, lastCheckBlock) conditional_order_generator C1 (OrderDiscoveryPoller) + C5 (CancellationWatcher) per-block SELECT with equality filters + ORDER BY lastCheckBlock
discrete_order_c3_status_idx(chainId, status, promotedAt) discrete_order C3 (OrderStatusTracker) per-block SELECT WHERE chainId + status='open' ORDER BY promotedAt
candidate_discrete_order_stale_idx(chainId, validTo) candidate_discrete_order C2 stale sweep WHERE chainId + validTo <= timestamp LIMIT 500

The old generator_check_block_active_idx(nextCheckBlock, status) is dropped — it didn't cover chainId or allCandidatesKnown, so it was rarely useful for C1/C5. The new index covers those queries end-to-end.

Validation

EXPLAIN ANALYZE on 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 — clean
  • pnpm typecheck — clean
  • pnpm test — 66/66 pass
  • After deploy: run EXPLAIN ANALYZE on the C1, C3, and C2 stale queries and confirm index scans

Closes COW-886

🤖 Generated with Claude Code

- 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>
@linear-code

linear-code Bot commented Jun 10, 2026

Copy link
Copy Markdown

COW-886

@lgahdl lgahdl left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 chainId and 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
  • c1c5PollIdx combines C1 and C5 into one index, which is appropriate since they differ only in allCandidatesKnown
  • c3StatusIdx includes promotedAt for the ORDER BY added 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 lgahdl left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@lgahdl lgahdl marked this pull request as ready for review June 10, 2026 21:24
lgahdl added a commit that referenced this pull request Jun 10, 2026
@jeffersonBastos

Copy link
Copy Markdown
Contributor

Superseded by #102 (final-qadevelop). Verified that this PR's changes (COW-886) are fully contained in the consolidated final-qa branch. Closing to merge everything through the single QA branch per the agreed plan — no work is lost.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants