Skip to content

perf: paginate discord-sync + push filters into SQL (#3163)#3190

Open
vedant7007 wants to merge 1 commit into
Priyanshu-byte-coder:mainfrom
vedant7007:fix/discord-sync-pagination-3163
Open

perf: paginate discord-sync + push filters into SQL (#3163)#3190
vedant7007 wants to merge 1 commit into
Priyanshu-byte-coder:mainfrom
vedant7007:fix/discord-sync-pagination-3163

Conversation

@vedant7007

Copy link
Copy Markdown

Summary

`/api/notifications/discord-sync` did one unbounded `.select()` and pulled every user with a discord webhook (webhook URL, timezone, mute state, last notification timestamp) into serverless memory in a single response — then serially walked each through 2-3 GitHub API roundtrips + a Supabase update. Both scaled with total user count, so once discord users cross a few hundred the outer loop can't finish within the Vercel function timeout and users at the tail get silently skipped every hour.

What changed

  • Adopted the pagination pattern already used by `/api/cron/sync` (`PAGE_SIZE=50`, deterministic `.order("id")`, `.range(page50, page50+49)`, break out when a short page returns).
  • Pushed the "hasn't been notified in the last 20 hours" and "not currently muted" filters into the SQL `WHERE` clause via `.or()` so we don't fetch rows we'll immediately skip. Cuts the wire payload per page down to actual candidates.
  • Cached `twentyHoursAgo` and `nowIso` outside the loop.

What's unchanged

The per-user `localHour` / weekday / streak-at-risk / weekly-summary / milestone / notification-sending logic is identical to the old code path. No behavior change for notification decisions — only the fetch/scan strategy differs.

Test plan

  • `tsc --noEmit` clean
  • verified the `.or()` clauses match the previous in-JS filter logic (NULL or condition satisfied)
  • once approved: manual smoke with a couple of test users to confirm pagination unwinds correctly on both an empty tail and a full page

Fixes #3163

…anshu-byte-coder#3163)

The discord notification cron loaded every user with a discord_webhook_url
in one unbounded query, then serially walked each through 2-3 GitHub API
roundtrips + a supabase update. Both scaled with total user count, so
once discord users cross a few hundred the outer loop couldn't finish
within the Vercel function timeout and users at the tail were silently
skipped.

- Adopt the same pagination pattern already used by /api/cron/sync:
  PAGE_SIZE=50, deterministic .order("id"), .range(page*50, page*50+49),
  break out when a short page is returned.
- Push the "hasn't been notified in the last 20 hours" and "not currently
  muted" filters into the SQL WHERE clause via .or() so we don't fetch
  rows we'll immediately skip. Cuts the wire payload per page down to
  actual candidates.
- Cache `twentyHoursAgo` and `nowIso` outside the loop so we don't
  recompute them per page.

Kept the per-user localHour / weekday / streak-at-risk / weekly-summary
logic intact — no behavior change for actual notification decisions.

Fixes Priyanshu-byte-coder#3163
@github-actions

Copy link
Copy Markdown

Hi @vedant7007 — thanks for the PR! 🙌

DevTrack asks contributors to ⭐ star the repo before a PR can be merged. It takes two seconds and is the easiest way to support the project.

Once you've starred, the star-required check turns green automatically (give it a moment). Thanks for contributing!

@github-actions github-actions Bot added type:bug GSSoC type bonus: bug fix type:feature GSSoC type bonus: new feature type:performance GSSoC type bonus: performance (+15 pts) gssoc26 GSSoC 2026 contribution labels Jul 12, 2026
@github-actions

Copy link
Copy Markdown

GSSoC Label Checklist 🏷️

@Priyanshu-byte-coder — please apply the appropriate labels before merging:

Difficulty (pick one):

  • level:beginner — 20 pts
  • level:intermediate — 35 pts
  • level:advanced — 55 pts
  • level:critical — 80 pts

Quality (optional):

  • quality:clean — ×1.2 multiplier
  • quality:exceptional — ×1.5 multiplier

Validation (required to score):

  • gssoc:approved — counts for points
  • gssoc:invalid / gssoc:spam / gssoc:ai-slop — does not score

Type labels (type:*) are auto-detected from files and title. Review and adjust if needed.
Points formula: (difficulty × quality_multiplier) + type_bonus

@vedant7007

Copy link
Copy Markdown
Author

starred the repo — should turn the star-required gate green on the next re-run 🌟

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

Labels

gssoc26 GSSoC 2026 contribution type:bug GSSoC type bonus: bug fix type:feature GSSoC type bonus: new feature type:performance GSSoC type bonus: performance (+15 pts)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Performance] /api/notifications/discord-sync fetches every discord user in a single query — cron truncates

1 participant