Skip to content

Optimize notification timestamp pagination#988

Merged
raymondjacobson merged 1 commit into
mainfrom
codex/notification-pagination-candidates
Jul 6, 2026
Merged

Optimize notification timestamp pagination#988
raymondjacobson merged 1 commit into
mainfrom
codex/notification-pagination-candidates

Conversation

@raymondjacobson

Copy link
Copy Markdown
Member

Summary

  • Bound timestamp-based notification pagination to the newest 25k single-recipient and 25k multi-recipient candidates before the expensive grouping/filtering step.
  • Keep the existing full query shape for non-timestamp notification loads.
  • Execute timestamp pagination in a read-only transaction with SET LOCAL enable_seqscan = off so the multi-recipient candidate branch uses the partial GIN path instead of the bad sequential scan plan.

Live evidence

  • Remaining post-rollout failures were full notification pagination (notifications query timed out), not unread polling.
  • On the hot production cursor (user_id=51, timestamp=1783054526.961588), the unbounded full page timed out under the 8s read guard.
  • The bounded 25k-per-branch query with the transaction-local planner setting returned a full 20-row page in ~2.1s.
  • The same bounded query without the planner setting still timed out under 8s, so the local setting is still needed for the multi-recipient overlap branch.
  • A 100k-per-branch cap crossed the 8s timeout, so 25k keeps useful margin while reducing truncation risk versus the 5k test.

Test plan

  • go test ./api -run ^'$'

@raymondjacobson raymondjacobson merged commit a76ef28 into main Jul 6, 2026
2 checks passed
@raymondjacobson raymondjacobson deleted the codex/notification-pagination-candidates branch July 6, 2026 22:41
raymondjacobson added a commit that referenced this pull request Jul 7, 2026
## Summary
- Bound unread notification polling to the newest 25k single-recipient
and 25k multi-recipient candidates before grouping.
- Bound first-page and timestamp notification reads to newest candidate
groups, then cap expensive validation/JSON aggregation to the newest 100
actions per selected group.
- Reuse the read-only transaction with scoped `SET LOCAL enable_seqscan
= off` for candidate-limited notification reads, including unread
polling.

## Why
- Today after PR #988, the remaining failures shifted to first-page
`limit=20` and unread `limit=0`, not just timestamp pagination.
- Since midnight Pacific on 2026-07-07, Cloud Logging showed 49
`notifications unread query timed out` and 13 `notifications query timed
out` messages.
- The hot first page for `eJ57D` includes giant groups: roughly 11.7k
`save:4281:type:playlist` rows and 12.3k `follow:51` rows inside the
newest candidate groups. Candidate rows alone were still too many for
the full validation/aggregation stack.

## Live validation
- Bounded unread query for `eJ57D` returned count 20 in ~0.6s.
- First-page candidate rows without action capping still timed out under
the 8s statement timeout when the full filter stack was included.
- First-page full filter stack with newest 100 actions per selected
group returned a full 20-group page in ~0.42s.

## Product impact
- Very large notification groups now return the newest 100 actions for
that group in candidate-limited reads instead of trying to return
thousands of action rows.
- The endpoint still returns the same response shape and group ordering,
but giant grouped notifications may include fewer action objects.

## Test plan
- `go test ./api -run ^'$'`\n- Attempted `go test ./api -run
TestV1Notifications`; blocked locally because the test DB at
`localhost:21300` is not running.
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