Skip to content

fix(api): cap /candles/:slab date-span to what MAX_BARS can hold [BUG-008] - #217

Open
Morenikeoa wants to merge 1 commit into
dcccrypto:mainfrom
Morenikeoa:fix/candles-max-date-span
Open

fix(api): cap /candles/:slab date-span to what MAX_BARS can hold [BUG-008]#217
Morenikeoa wants to merge 1 commit into
dcccrypto:mainfrom
Morenikeoa:fix/candles-max-date-span

Conversation

@Morenikeoa

Copy link
Copy Markdown

Problem

GET /candles/:slab validates from < to but never bounds the span between them. The existing protection — .limit(MAX_BARS * 10) (50,000 rows) on the trades query — only bounds the result size that comes back, not the work Postgres has to do to produce it: the query filters by slab_address + network, orders by created_at ascending, and takes the first 50,000 rows. For a multi-year requested range on a high-volume market with more historical trades than that, the DB still has to scan/sort the full matching row set for that date range to find the first 50,000 in order, regardless of how small the eventual response ends up being after bucketing.

Fix

Added a max-span check: MAX_BARS * bucketSeconds. This scales with the requested resolution rather than being a flat constant — a flat cap would be wrong at either end (too restrictive for daily candles, too permissive for 1-minute candles). At 1-minute resolution the max span is ~3.5 days; at daily resolution it's ~13.7 years — both are exactly "the widest range that could still produce ≤ MAX_BARS buckets," which is the actual invariant this protects.

Two existing tests used from=0&to=9999999999 as a "don't care about the exact range" placeholder spanning ~316 years; narrowed both to realistic windows since their actual intent was exercising basic data flow with a mocked Supabase response, not wide-range behavior.

Proof of Fix

New tests: a multi-year range at 1-minute resolution is rejected with a 400 before the Supabase query is ever issued; a range exactly at the computed max span is accepted; a multi-year range at daily resolution (where it's legitimately within bounds) is accepted.

Verified this is a genuine regression test: reverted just the source change and reran — the multi-year-range test failed with a 200 instead of 400 (the query would have gone through unbounded). Restored the fix and it passes.

  • All existing tests pass — output attached.
  • New regression tests pass against the fix, the core one fails against pre-fix code (verified locally).
  • tsc --noEmit clean (no separate lint script in this repo).

Test Output

✓ tests/routes/candles.test.ts (12 tests) 58ms

Full suite: 297/298 passed (294 baseline + 3 new). The 1 failure (tests/sdk-smoke.test.ts) is pre-existing and unrelated — it asserts on an exact @percolatorct/sdk error-message string that has drifted from the locally-resolved SDK version in this environment.

Related

Found during a broader API audit. Distinct from open issue #201 (MAX_BARS not capping the output bar count even with the row limit applied) — that's a separate root cause (output bucket-count bound vs. this fix's input date-span bound); both are needed, neither subsumes the other. No existing open issue/PR covers this specific date-span gap.

…-008]

The route validated from < to but never bounded the SPAN. The existing
row cap (MAX_BARS * 10 = 50,000 rows via .limit()) only bounds the
RESULT size — Postgres still has to scan/sort the full set of matching
rows for the requested date range (filtered by slab + network, ordered
by created_at ascending) to find the first 50,000, which is unbounded
work for a multi-year range on a high-volume market regardless of how
few rows are eventually returned or how small the final candle count is.

Cap the span to MAX_BARS * bucketSeconds — scaled per resolution, so the
limit is proportional to what could actually produce MAX_BARS buckets at
that resolution (~3.5 days at 1-minute resolution, ~13.7 years at daily),
rather than a flat arbitrary cutoff that would be wrong at either end.

Two existing tests used from=0&to=9999999999 as a "don't care about the
range" placeholder; narrowed to realistic windows since their actual
intent was exercising basic data flow, not wide-range behavior.

Added a regression test (multi-year range at 1-minute resolution is
rejected without ever calling the DB; exact-max-span and resolution-scaling
are accepted). Verified it fails against the pre-fix code (200 instead of
400) and passes against the fix.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 25, 2026

Copy link
Copy Markdown

@Princessdada is attempting to deploy a commit to the Khubair Nasir's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@Morenikeoa, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 28 minutes and 3 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6ac80bad-6c5c-4bf7-a411-ab64f46f9bf6

📥 Commits

Reviewing files that changed from the base of the PR and between b2751f4 and 08eb942.

📒 Files selected for processing (2)
  • src/routes/candles.ts
  • tests/routes/candles.test.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

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.

1 participant