fix: the Running exchange filter took 24 seconds - #281
Conversation
Measured on 1M exchanges: 24,185ms to 229ms. Same rows, same counts.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository: simplify9/coderabbit/.coderabbit.yaml Review profile: ASSERTIVE Plan: Team Run ID: 📒 Files selected for processing (1)
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review. 📜 Recent review details🔇 Additional comments (1)
📝 WalkthroughSummary
Performance
Riskrisk:low The query semantics remain unchanged. The main risk is an incorrect result-row correlation in the Security-sensitive areasNo security-sensitive logic changed. Test coverage impactValidation used performance testing and compared all four status pill totals before and after. No new automated test coverage is reported. Operational concerns
WalkthroughThe status ChangesExchange search filtering
Estimated code review effort: 2 (Simple) | ~10 minutes Merge Risk: ⚪ Minimal · up to The Running exchange filter now uses an equivalent no-result check, reducing query time without changing permissions or the response contract. No actionable merge-blocking risk remains after normal checks and review. Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
The Running pill on the Exchanges page took ~24 seconds to return 25 rows.
Why
"Running" means "no result row yet", and it was asked as
Status == null— which reads asxchange_result.success IS NULLon a left join. Postgres can't estimate how many rows survive that, so it guessed 1 when the real answer was 29,991. Believing one row would come out, it planned every join above it for one row and chose a per-row sequential scan ofxchange_aggregation— running that scan 29,991 times, for 593 million wasted row comparisons.The change
Ask the same question as
NOT EXISTSinstead. Postgres recognises that as an anti-join and estimates it correctly.Equivalent because
xchange_result.successisNOT NULL, so "no result row" is the only way the status can be null.Measured
On a throwaway database seeded with 1,000,000 exchanges, through the real API:
All four status pills return identical totals before and after (29,991 / 819,196 / 43,011 / 107,802).
Not in this PR
The footer's exact-count query is now the whole remaining cost of a filtered search (~200 ms per 800k rows counted) and scales with table size. That's the next thing to look at.
🤖 Generated with Claude Code