Skip to content

fix: the Running exchange filter took 24 seconds - #281

Merged
hamzahalq merged 1 commit into
releases/r10.0from
hamza/perf/exchange-search
Sep 1, 2026
Merged

fix: the Running exchange filter took 24 seconds#281
hamzahalq merged 1 commit into
releases/r10.0from
hamza/perf/exchange-search

Conversation

@hamzahalq

Copy link
Copy Markdown
Contributor

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 as xchange_result.success IS NULL on 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 of xchange_aggregation — running that scan 29,991 times, for 593 million wasted row comparisons.

The change

Ask the same question as NOT EXISTS instead. Postgres recognises that as an anti-join and estimates it correctly.

Equivalent because xchange_result.success is NOT 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:

before after
Running pill, end to end 24,185 ms 229 ms
— rows query 23,972 ms 14 ms

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

Measured on 1M exchanges: 24,185ms to 229ms. Same rows, same counts.
@coderabbitai

coderabbitai Bot commented Sep 1, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: simplify9/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Team

Run ID: bfca1e13-986f-47ad-b4ef-e9338fc6cb8e

📥 Commits

Reviewing files that changed from the base of the PR and between 430ce7e and 8d93763.

📒 Files selected for processing (1)
  • SW.Bitween.Api/Resources/Xchanges/Search.cs

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.

📜 Recent review details
🔇 Additional comments (1)
SW.Bitween.Api/Resources/Xchanges/Search.cs (1)

130-139: LGTM!


📝 Walkthrough

Summary

  • Changed the Running exchange filter in SW.Bitween.Api/Resources/Xchanges/Search.cs from Status == null to a NOT EXISTS check for XchangeResult.
  • The change preserves results because xchange_result.success is non-null.
  • PostgreSQL can optimize the condition as an anti-join.

Performance

  • Running filter: 24,185 ms to 229 ms on 1,000,000 exchanges.
  • Rows query: 23,972 ms to 14 ms.
  • Status pill totals remained unchanged.

Risk

risk:low

The query semantics remain unchanged. The main risk is an incorrect result-row correlation in the NOT EXISTS condition.

Security-sensitive areas

No security-sensitive logic changed.

Test coverage impact

Validation used performance testing and compared all four status pill totals before and after. No new automated test coverage is reported.

Operational concerns

  • No database migration or deployment procedure change is required.
  • Rollback consists of reverting the query change.
  • The footer exact-count query remains the primary cost of filtered searches and is outside this change.

Walkthrough

The status "0" filter in the exchange search now uses NOT EXISTS to identify exchanges without an XchangeResult row.

Changes

Exchange search filtering

Layer / File(s) Summary
Still-running filter query
SW.Bitween.Api/Resources/Xchanges/Search.cs
The status "0" filter now checks for the absence of an XchangeResult record instead of checking for Status == null.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to 8d937

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: database, risk:medium

Suggested reviewers: ahmadrabuhussein

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 1 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the Running exchange filter performance fix.
Description check ✅ Passed The description directly explains the filter change, its rationale, measured performance improvement, and scope.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
  • Fix all pre-merge checks with AI

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@hamzahalq
hamzahalq merged commit 2581fa8 into releases/r10.0 Sep 1, 2026
5 checks passed
@hamzahalq
hamzahalq deleted the hamza/perf/exchange-search branch September 1, 2026 11:47
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