Skip to content

fix(leaderboard): page through daily_breakdowns so wide date ranges don't drop users - #97

Open
yoo-minho wants to merge 1 commit into
sculptdotfun:mainfrom
yoo-minho:fix/date-range-truncation
Open

fix(leaderboard): page through daily_breakdowns so wide date ranges don't drop users#97
yoo-minho wants to merge 1 commit into
sculptdotfun:mainfrom
yoo-minho:fix/date-range-truncation

Conversation

@yoo-minho

Copy link
Copy Markdown

Widening the date filter makes the leaderboard smaller, and high-spend accounts disappear from it.

Repro

range rows returned bottom entry
?from=2026-07-18&to=2026-07-31 (14d) 100 β€”
?from=2026-07-01&to=2026-07-31 (31d) 52 $13.40

Accounts with five figures of spend in that window are absent from the 31d board while $13 entries are present. A wider window returning fewer rows, with big spenders missing and tiny ones kept, does not look like ranking.

Cause

getLeaderboardByDateRange pulls every matching daily row in one call:

const { data: allDailyBreakdowns } = await this.client
  .from("daily_breakdowns")
  .select("*")
  .in("submission_id", submissionIds)
  .gte("date", params.dateFrom)
  .lte("date", params.dateTo)
  .limit(50000);

PostgREST caps rows server-side (db-max-rows), so .limit(50000) is silently truncated once the window is wide enough to exceed it.

Truncation is not a partial total. A submission whose rows land past the cut ends up with zero daily rows, and the loop below skips it:

const filteredDaily = dailyBySubmission.get(submission.id) || [];
if (filteredDaily.length === 0) continue;

so the user drops off the board entirely rather than showing a smaller number. There is also no .order(), so which submissions survive the cut is arbitrary.

Row count scales with window width Γ— submissions, which is why 7d and 14d look fine and 30d does not.

Fix

Page through with a stable order (submission_id, date), bounded at 200 pages (200k rows) so a pathological range cannot spin.

Aggregating in the DB (an RPC that returns SUM(...) GROUP BY submission_id for the window) would be the leaner long-term shape since it avoids shipping every daily row to the client at all β€” happy to follow up with that if you prefer it, but this keeps the change contained to the bug.

Checks

  • pnpm exec tsc --noEmit: 14 pre-existing errors on main, still 14 after β€” none in the touched file.
  • pnpm build: compiles successfully. Page-data collection fails on both main and this branch without Supabase env vars (different route each run), so unrelated to this change.

…on't drop users

`getLeaderboardByDateRange` fetches every matching daily row in one call with
`.limit(50000)`. PostgREST caps rows server-side (`db-max-rows`), so that limit
is silently truncated on wider windows.

Truncation isn't a partial total. A submission whose rows fall past the cut ends
up with zero daily rows and is skipped by the `filteredDaily.length === 0` guard,
so the user vanishes from the board entirely. And with no `.order()`, which
submissions survive is arbitrary.

The visible symptom is that widening the window *shrinks* the board:

  from=2026-07-18&to=2026-07-31  (14d) β†’ 100 rows
  from=2026-07-01&to=2026-07-31  (31d) β†’  52 rows, bottom entry $13.40

Accounts with five figures of spend are missing from the 31d board while $13
entries are present, which is consistent with a row cap hit partway through
rather than with ranking.

Page through with a stable order instead. Bounded at 200 pages (200k rows) so a
pathological range can't spin.
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