Skip to content

fix: schedule bounded cleanup of expired rate-limit buckets - #301

Open
amanthanvi wants to merge 2 commits into
mainfrom
cursor/schedule-rate-limit-cleanup-8c8b
Open

amanthanvi wants to merge 2 commits into
mainfrom
cursor/schedule-rate-limit-cleanup-8c8b

Conversation

@amanthanvi

@amanthanvi amanthanvi commented Oct 10, 2026 •

Copy link
Copy Markdown
Owner

What

  • Register convex/crons.ts to run internal rateLimit.cleanupExpired every minute.
  • Keep each deletion inside one mutation (at most 500 rows) and schedule at most 40 follow-up batches while expired rows remain. The next minute continues any remainder.
  • cleanupExpired stays an internalMutation and still reads the clock itself.

Why

rateLimitBuckets keys embed the client identifier. cleanupExpired already existed, but nothing called it, so expired rows and those identifiers were kept indefinitely. A separate change is locking down rateLimit.enforce; this PR only schedules and bounds the cleanup.

Checklist

  • pnpm next:lint, pnpm next:grammar, pnpm next:test (Next.js or Convex changes)
  • pnpm ingest:lint, pnpm ingest:test (ingestion changes)
  • CHANGELOG.md updated under Unreleased when behavior changes

Local checks: pnpm convex:test (127 passed), pnpm convex:typecheck, scripts/check-convex-public-api.mjs, pnpm next:grammar, pnpm next:lint, and pnpm next:test (86 passed). The local convex dev watcher accepted the cron module, and cleanupExpired is deployed as an internal mutation.

Open in Web Open in Cursor 

Summary by Sourcery

Schedule bounded recurring cleanup of expired rate-limit buckets while preserving the internal mutation and server-side clock handling.

Bug Fixes:

  • Automatically remove expired rate-limit buckets so client identifiers are not retained indefinitely.

Enhancements:

  • Bound cleanup transactions and continue larger backlogs through limited follow-up batches and subsequent cron runs.

Tests:

  • Add coverage for cron registration, expiration handling, batching limits, backlog draining, and follow-up exhaustion.

Expired buckets kept raw client identifiers and nothing deleted them.
Register a one-minute internal cron that removes them in capped batches
and schedules a bounded number of follow-ups while any remain.

Co-authored-by: Aman Thanvi <amanthanvi@users.noreply.github.com>
@cursor

cursor Bot commented Oct 10, 2026

Copy link
Copy Markdown

@coderabbitai review

@greptileai review

@vercel

vercel Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
betterman Ready Ready Preview Oct 10, 2026 9:16pm UTC

Request Review

@coderabbitai

coderabbitai Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 6e4dd275-f167-4105-bd40-bf1b1ca9e84c

📥 Commits

Reviewing files that changed from the base of the PR and between 449ba80 and 80d3d50.


⛔ Files ignored due to path filters (1)
  • convex/_generated/api.d.ts is excluded by !**/_generated/**

📒 Files selected for processing (4)
  • CHANGELOG.md
  • convex/crons.ts
  • convex/rateLimit.test.ts
  • convex/rateLimit.ts

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


📜 Recent review details
🧰 Additional context used
📚 Code guidelines (2)
AGENTS.md — auto-discovered
convex/_generated/ai/guidelines.md — auto-discovered

📓 Path-based instructions (2)
Source excerpt: When working on Convex code, **always read `convex/_generated/ai/guidelines.md` first** for important guidelines on how to correctly use Convex APIs and patterns.

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • convex/crons.ts
  • convex/rateLimit.ts
  • convex/rateLimit.test.ts

Source excerpt: HTTP endpoints are defined in `convex/http.ts` and require an `httpAction` decorator.

📄 CodeRabbit inference engine (convex/_generated/ai/guidelines.md)

Files:

  • convex/crons.ts
  • convex/rateLimit.ts
  • convex/rateLimit.test.ts

🔇 Additional comments (4)
convex/rateLimit.ts (1)

2-2: LGTM!

Also applies to: 52-70, 74-75, 79-79, 81-85, 87-88, 90-90, 100-109


convex/rateLimit.test.ts (1)

1-163: LGTM!


convex/crons.ts (1)

1-20: LGTM!


CHANGELOG.md (1)

7-7: LGTM!



📝 Summary

Summary by CodeRabbit

  • Privacy
    • Expired rate-limit data is now cleaned up every minute in bounded batches. Remaining expired data is processed in subsequent batches, while active data is retained.

Walkthrough

The rate-limit cleanup mutation now deletes expired buckets in bounded batches and can schedule follow-up runs. A cron invokes cleanup every minute. Tests cover expiration rules, batch limits, and follow-up budgets.

Changes

Expired Rate-Limit Cleanup

Layer / File(s) Summary
Bounded cleanup and follow-ups
convex/rateLimit.ts, convex/rateLimit.test.ts
The internal mutation reads the clock and deletes expired buckets in bounded batches. It schedules follow-ups when more buckets remain and the follow-up budget is positive. Tests cover expiration boundaries, backlog processing, and batch limits.
Minute schedule and configuration checks
convex/crons.ts, convex/rateLimit.test.ts, CHANGELOG.md
The cron invokes cleanup every minute with configured limits. Tests verify the cron schedule and internal mutation. The changelog describes the cleanup cadence and bounded batches.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant CronScheduler
  participant cleanupExpired
  participant RateLimitBuckets
  CronScheduler->>cleanupExpired: invoke scheduled cleanup
  cleanupExpired->>RateLimitBuckets: delete expired buckets in a batch
  cleanupExpired->>cleanupExpired: schedule follow-up when more buckets remain and budget is positive
Loading

Merge Risk: ⚪ Minimal · up to 80d3d

The scheduled cleanup appears ready to merge after normal checks. No concrete issue introduced by this change was established.

Pre-merge checks | Passed 4 | Failed 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 6 functions across 3 files. (1 skipped: 1 … 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 and concisely describes the main change: scheduling bounded cleanup for expired rate-limit buckets.
Description check Passed The description includes the required What, Why, and Checklist sections. It explains the cleanup behavior, motivation, scope, and relevant validation results. The ingestion checks remain unchecked bec…
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.

Full details: Docstring Coverage

Explanation

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 6 functions across 3 files. (1 skipped: 1 unsupported.)


  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR


🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

A rabbit checks the ticking clock
Expired buckets hop away
Small batches clear the burrow
More follow when the budget says
Each minute brings another sweep
The live ones safely stay

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

@sourcery-ai

sourcery-ai Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Reviewer's Guide

Adds a one-minute cron for internal, clock-driven rate-limit bucket cleanup, with transaction-safe batches capped at 500 rows and a maximum of 40 immediate follow-ups before the next cron tick resumes remaining work; comprehensive tests and changelog documentation are included.

Sequence diagram for bounded rate-limit bucket cleanup

sequenceDiagram
    participant Cron
    participant Cleanup as rateLimit.cleanupExpired
    participant DB as rateLimitBuckets
    participant Scheduler

    Cron->>Cleanup: cleanupExpired(maxBuckets, followupsRemaining)
    Cleanup->>DB: query expired buckets with by_expiresAt
    DB-->>Cleanup: up to maxBuckets + 1 rows
    Cleanup->>DB: delete up to maxBuckets rows
    alt expired rows remain and followupsRemaining > 0
        Cleanup->>Scheduler: runAfter(0, cleanupExpired, maxBuckets, followupsRemaining - 1)
    end
    Cleanup-->>Cron: deleted, hasMore, scheduledFollowup
Loading

File-Level Changes

Change Details Files
Schedule recurring cleanup of expired rate-limit buckets.
  • Register an internal cleanup mutation on a one-minute interval.
  • Pass bounded batch and follow-up limits from the cron configuration.
convex/crons.ts
convex/_generated/api.d.ts
Bound cleanup work and continue backlog safely across mutations.
  • Clamp batch size to 1–500 with a default of 100.
  • Read expiration time inside the mutation and delete only expired rows.
  • Detect remaining rows using an extra fetched record and schedule up to 40 zero-delay follow-ups.
  • Return deletion, remainder, and scheduling status while preserving internal-only access.
convex/rateLimit.ts
Add coverage for scheduling, expiration behavior, batching, and chaining limits.
  • Verify cron registration and internal mutation visibility.
  • Test preservation of live buckets, exact-page termination, backlog draining, follow-up exhaustion, and argument clamping.
convex/rateLimit.test.ts
Document the privacy-impacting cleanup behavior.
  • Add an Unreleased changelog entry describing bounded periodic deletion of expired buckets and embedded client identifiers.
CHANGELOG.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@amanthanvi
amanthanvi marked this pull request as ready for review October 10, 2026 21:12
Copilot AI balanced review requested due to automatic review settings October 10, 2026 21:12

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="convex/rateLimit.ts" line_range="55" />
<code_context>
+// One mutation stays under Convex's per-transaction document limits.
+// The cron asks for the cap; a caller that omits maxBuckets deletes a smaller page.
+export const CLEANUP_BATCH_DEFAULT = 100;
+export const CLEANUP_BATCH_LIMIT = 500;
+// Further batches one wave may schedule after the current mutation. The next
+// cron tick continues any remainder, so a stuck hasMore cannot loop forever.
</code_context>
<issue_to_address>
**Expired identifiers accumulate**

When expired buckets are created faster than cleanup can delete 20,500 per minute over a sustained period, `cleanupExpired` cannot keep pace, so the backlog grows and client identifiers remain stored past their expiration.

Increase cleanup capacity enough to keep pace with sustained bucket creation, or ensure bucket creation stays below that capacity.

Also at `convex/rateLimit.ts:58`, `convex/crons.ts:12`, `convex/crons.ts:16`.
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 1 finding to address first, and the cron permanently deletes expired rate-limit bucket rows containing raw client identifiers, so a mistaken one-minute retention policy would remove data that reverting the change cannot restore. The cleanup is bounded per transaction but continues indefinitely across cron runs, making the irreversible effect potentially broad while providing no failure signal if the retention decision is wrong.

Blocking findings: convex/rateLimit.ts:55


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread convex/rateLimit.ts
@greptile-apps

greptile-apps Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

[High impact] The PR appears safe to merge, with no new actionable issues found.

Summary

This PR schedules bounded cleanup of expired rate-limit buckets.

  • Expired rate-limit buckets are cleared in bounded, recurring batches.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Every-minute cron] --> B[Read up to 501 expired buckets]
  B --> C[Delete at most 500 buckets]
  C --> D{More rows and follow-ups left?}
  D -->|Yes| E[Schedule another batch with one fewer follow-up]
  E --> B
  D -->|No| F[Stop; next cron run continues any remainder]
Loading

Reviews (2) · Last reviewed commit: "docs: describe rate-limit cleanup as per..." · Reviewed by Greptile

Comment thread CHANGELOG.md Outdated
Expired identifiers stay until a later bounded batch when a backlog
exceeds one cron wave. Say that in the changelog and the cron comment.

Co-authored-by: Aman Thanvi <amanthanvi@users.noreply.github.com>
@cursor

cursor Bot commented Oct 10, 2026

Copy link
Copy Markdown

@coderabbitai review

@greptileai review

@amanthanvi

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@amanthanvi

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@amanthanvi

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

This branch was successfully deployed

1 active deployment
Preview — 80d3d501 Deployed Oct 10, 2026 by vercel[bot]
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.

3 participants