Uh oh!
There was an error while loading. Please reload this page.
fix(limits): retry_after ceiling capped at window — deterministic on coarse timers (#346) - #351
Conversation
Warning Review limit reached
Next review available in:35 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the 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 reviews. How do review 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 refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThe rate limiters now calculate retry delays with ceiling semantics and cap them at the configured window. Tests cover coincident timestamps, near-window expiry, reset behavior, limit enforcement, and per-key isolation. ChangesRate-limit retry behavior
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
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. Comment |
Deploying with |
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs | frontend-staging | 6731cd7 | Commit Preview URL Branch Preview URL | Jul 29 2026, 09:10 AM |
…coarse timers (#346) Rebased onto current main: main had already adopted math.ceil in the flashcard limiter; this keeps that and adds the min(window, ...) cap to BOTH sliding-window limiters (services/request_limits.py still had the old int(...) + 1 overshoot), plus regression tests for coincident timestamps and sub-second remainders. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
a0cf1f9 to
b0716afCompare… align twin comments Review follow-ups: request_limits.py's comment now names the negative- elapsed (NTP step) case the cap protects against, matching its twin; a regression test in both limiter test files freezes time backward so a future revert of the cap fails loudly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Uh oh!
There was an error while loading. Please reload this page.
Problem
check_rate_limitcomputes the retry hint asint(window - elapsed) + 1— a broken ceiling. When the rate-limited call shares an identicaltime.time()value with the first call in the window,elapsed == 0.0exactly and the hint overshoots to window + 1 (61s for a 60s window). On Windows this happens reliably (the timer ticks every ~15.6 ms, so a tight loop of 6 calls lands on one tick), which:(0, window]contract callers surface asRetry-After, andTestRateLimit::test_sixth_call_returns_retry_afterfail out-of-the-box for every Windows contributor (assert 61 <= 60). Linux CI passes only by accident — elapsed there is tiny-but-nonzero, soint(59.999…) + 1 == 60.Fix
True ceiling semantics, clamped at the window, in both copies of the limiter:
services/request_limits.py(shared limiter behind OCR extraction + Gradescope routes)services/flashcard_import_service.py(older twin the shared module's docstring notes should eventually migrate)The clamp also guards the pathological
elapsed < 0case (clock steps backward between calls).Tests (TDD — written first, verified failing)
test_retry_capped_at_window_on_coincident_timestamps(both limiters): freezestime.timesoelapsed == 0.0exactly — deterministic reproduction of the Windows failure on every platform; pinsretry == 60. Failed (61) before the fix, passes after.test_retry_uses_ceiling_of_remaining_window(both limiters): 0.5s remaining →retry == 1, pinning ceiling semantics against a regression to plain truncation.tests/test_request_limits.pyalso covers the shared limiter's base contract (allow-up-to-limit, over-limit hint bounds, key isolation, window reset), which previously had no direct unit tests.Verification
test_flashcard_import_service,test_request_limits,test_extract_auth_bounds,test_gradescope): 72/72 passed — on the Windows machine where the suite was previously red.ruff checkclean on all touched files.Supersedes #347 (same clamp idea, but closed without regression tests; this PR adds the deterministic frozen-time coverage the issue asked for).
Fixes#346
🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
Tests