Skip to content

fix(rate-limit): restore Keyv limiter compatibility contracts - #501

Merged
KooshaPari merged 3 commits into
mainfrom
fix/rate-limiter-keyv-contract-20260805
Aug 7, 2026
Merged

KooshaPari merged 3 commits into
mainfrom
fix/rate-limiter-keyv-contract-20260805

Conversation

@KooshaPari

@KooshaPari KooshaPari commented Aug 5, 2026 •

Copy link
Copy Markdown
Owner

User description

Scope

Applies the isolated f3b8ac89 rate-limiter extraction only: src/shared/utils/rateLimiter.ts. It deliberately does not adopt the broad preserved fix/keyv-limiter-contracts-20260804 branch.

Provenance

  • Source extraction: f3b8ac89d0013fbc22fe3df3a120c40440e0898a
  • Original preserved change: 34ff907347
  • Applied commit: 8b8c53c62e972118e7199ab9bd7e947dc2f4f5b3

Validation

  • git diff --check origin/main...HEAD
  • Focused Node rate-limiter suite: 10 passed, 0 failed
  • vitest run --config vitest.config.ts tests/e2e/rate-limit.e2e.ts: 4 passed, 0 failed

CodeAnt-AI Description

Restore compatible rate-limit behavior across API-key policies

What Changed

  • API-key requests are now checked against every configured time window, and all counters increase only when the request passes every limit
  • Rate limiting uses persistent Keyv/SQLite storage only when explicitly configured; otherwise it uses bounded process-local protection without attempting an implicit Redis connection
  • Existing single-window callers retain their remaining-count and reset-time responses
  • Invalid limits and window sizes now return clear configuration errors

Impact

✅ Consistent enforcement across multiple rate-limit windows
✅ Fewer unexpected Redis connection failures
✅ Clearer invalid rate-limit configuration errors

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

Summary by CodeRabbit

  • New Features

    • Added support for applying multiple rate-limit rules to requests.
    • Added detailed rate-limit results, including remaining capacity, reset timing, and the rule that was exceeded.
    • Added safeguards for unavailable persistence services, with bounded fallback storage.
    • Added tools for resetting, cleaning up, and testing rate-limit state.
  • Bug Fixes

    • Prevented partial request counts when any configured rule rejects a request.
    • Improved cleanup of expired rate-limit entries.

(cherry picked from commit 34ff907)
(cherry picked from commit f3b8ac8)
Copilot AI lite review requested due to automatic review settings August 5, 2026 05:33
@codeant-ai

codeant-ai Bot commented Aug 5, 2026 •

Copy link
Copy Markdown

🤖 CodeAnt AI — Review Status

Status Commit Started (UTC) Finished (UTC)
✅ Reviewed your PR 8b8c53c Aug 05, 2026 · 05:33 05:37

@codeant-ai

codeant-ai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

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.

@codeant-ai codeant-ai Bot added the size:L This PR changes 100-499 lines, ignoring generated files label Aug 5, 2026
@mergify mergify Bot added the typescript label Aug 5, 2026
@coderabbitai

coderabbitai Bot commented Aug 5, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@KooshaPari, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 7 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 19049fb8-8175-4894-b393-82803f173f88

📥 Commits

Reviewing files that changed from the base of the PR and between 8b8c53c and d9f0e39.

📒 Files selected for processing (3)
  • src/shared/utils/rateLimiter.ts
  • tests/unit/rate-limiter-fallback.test.ts
  • tests/unit/rate-limiter-keyv-sqlite.test.ts

Note

.coderabbit.yaml has unrecognized properties

CodeRabbit is using all valid settings from your configuration. Unrecognized properties (listed below) have been ignored and may indicate typos or deprecated fields that can be removed.

⚠️ Parsing warnings (1)
Validation error: Unrecognized key: "review"
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
📝 Walkthrough

Walkthrough

The rate limiter now supports validated fixed-window rule arrays and the existing positional API. It selects Keyv/SQLite only when REDIS_URL is configured, uses bounded in-memory fallback stores, validates all windows before increments, and exposes reset, cleanup, eviction, and test-mode controls.

Changes

Rate limiter redesign

Layer / File(s) Summary
Storage and result contracts
src/shared/utils/rateLimiter.ts
Adds RateLimitRule, optional result metadata, explicit Redis configuration checks, Keyv storage setup, test mode, stale-window eviction, reset, and cleanup behavior.
Atomic in-memory rule evaluation
src/shared/utils/rateLimiter.ts
Validates rule values and checks all in-memory windows before incrementing any counter.
Persistent and public multi-rule flow
src/shared/utils/rateLimiter.ts
Adds asynchronous Keyv multi-window checks, local fallback protection, overloaded checkRateLimit, checkRateLimitWithRules, and checkRateLimitArray. Removes the legacy single-rule wrapper.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant checkRateLimit
  participant Keyv
  participant LocalProtection
  Caller->>checkRateLimit: submit key and rules
  checkRateLimit->>Keyv: validate and increment windows
  Keyv-->>checkRateLimit: counters or persistence error
  checkRateLimit->>LocalProtection: apply fallback protection
  checkRateLimit-->>Caller: return rate-limit result
Loading

Possibly related PRs

Suggested reviewers: copilot, diegosouzapw

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the scope and validation, but it omits most required template sections, including issues, tests, coverage, reviewer notes, and pillar checks. Complete the required template sections and document test files, coverage impact, reviewer risks, related issues, and 71-pillar checks.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the primary change: restoring Keyv rate-limiter compatibility contracts.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/rate-limiter-keyv-contract-20260805

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

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

Comment thread src/shared/utils/rateLimiter.ts
Comment on lines +159 to +169
const counts = await Promise.all(keys.map((key) => store.get<number>(key)));
for (let index = 0; index < rules.length; index += 1) {
if ((counts[index] ?? 0) >= rules[index].limit) {
return { allowed: false, failedWindow: rules[index].window };
}
}
await Promise.all(
rules.map((rule, index) => {
const remainingMs = (Math.floor(nowSeconds / rule.window) + 1) * rule.window * 1000 - nowMs;
return store.set(keys[index], (counts[index] ?? 0) + 1, remainingMs + 1000);
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: The persistent path performs a non-atomic read/check followed by independent writes. Concurrent requests can read the same counts and overwrite each other's increments, allowing more requests than the configured limits and violating the all-windows increment contract. Use an atomic transaction or backend-supported increment-and-check operation. [race condition]

Severity Level: Critical 🚨
- ❌ Concurrent API-key requests can exceed configured limits.
- ⚠️ Multi-window enforcement is not atomic across instances.
- ⚠️ Rate-limit rejection at apiKeyPolicy.ts:657-668 can be bypassed.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/shared/utils/rateLimiter.ts
**Line:** 159:169
**Comment:**
	*Race Condition: The persistent path performs a non-atomic read/check followed by independent writes. Concurrent requests can read the same counts and overwrite each other's increments, allowing more requests than the configured limits and violating the all-windows increment contract. Use an atomic transaction or backend-supported increment-and-check operation.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Partially fixed in 44122b7: serialized Keyv check-and-write execution prevents the demonstrated concurrent interleaving within a running server process; the new regression admits exactly one of 12 simultaneous requests at limit 1. Cross-process atomicity requires a transactional backend API not exposed by @keyv/sqlite, so this thread remains open rather than claiming that broader guarantee.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8b8c53c62e

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/shared/utils/rateLimiter.ts

@coderabbitai coderabbitai 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.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/shared/utils/rateLimiter.ts`:
- Around line 220-231: Add automated tests for checkRateLimitWithRules covering
Keyv failure fallback, duplicate-rule handling, bounded local state, and
concurrent persistent checks. Reuse the existing rate-limiter test setup and
assert each scenario’s allowed/limited behavior, ensuring the production changes
in src/shared/utils/rateLimiter.ts are exercised.
- Around line 121-130: Update validateRules and the public rate-limiter entry
points to validate complete inputs with the shared Zod schemas from
src/shared/validation/schemas.ts. Parse suppliedRules and keyId before
generating keys or mutating limiter state, and use the parsed values throughout;
remove the manual rule-validation path where the schema provides equivalent
validation.
- Around line 121-130: Update validateRules to reject duplicate rule.window
values during validation, tracking windows seen while mapping or validating
rules and throwing a TypeError for repeats; preserve the existing
positive-integer validation and return behavior for unique rules.
- Around line 56-59: Bound both FALLBACK_MEMORY_STORE and positionalCounters
with expiry-aware storage and a hard capacity. Update their insertion and
cleanup paths so expired windows are removed and capacity pressure evicts only
inactive/expired entries, never active rate-limit state; retain
TEST_MEMORY_STORE behavior unless required by the shared storage implementation.
Apply the change consistently to the related paths referenced in the diff, using
EVICTION_THRESHOLD or the established capacity configuration.
- Around line 75-80: Update getRedisClient() to return the existing
Keyv/SQLite-backed Redis compatibility adapter when Redis is configured, instead
of unconditionally throwing “Redis is not available.” Preserve the “Redis is not
configured” error for unconfigured environments and ensure the returned adapter
supports the get, set, and del operations used by apiKeys.ts and apiKeyCache.ts.
- Around line 152-175: Move getKeyvStore() inside the try block of
checkKeyvRateLimit so initialization failures reach the fallback handler. Change
the catch to receive unknown errors, log the sanitized error via
sanitizeErrorMessage() with the existing pino context, then invoke
checkInMemoryRateLimit(FALLBACK_MEMORY_STORE, keyId, rules).
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 10f37c45-9bb8-4a25-8660-474914041bf1

📥 Commits

Reviewing files that changed from the base of the PR and between 92fafe8 and 8b8c53c.

📒 Files selected for processing (1)
  • src/shared/utils/rateLimiter.ts

Comment thread src/shared/utils/rateLimiter.ts Outdated
Comment thread src/shared/utils/rateLimiter.ts
Comment thread src/shared/utils/rateLimiter.ts
Comment thread src/shared/utils/rateLimiter.ts
Comment thread src/shared/utils/rateLimiter.ts
@sonarqubecloud

sonarqubecloud Bot commented Aug 5, 2026

Copy link
Copy Markdown

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown

L17 Latency Regression Report

No trace file available — cannot compute regression

Threshold: 10% p99 regression.

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown

L17 Latency Budget Report

--- Latency Budget Summary ---
  Total endpoints checked: 0
  Passed: 0
  Warnings: 0
  Failures: 0

Checked against: budgets/rest-endpoints.yaml.

@KooshaPari
KooshaPari merged commit 0220572 into main Aug 7, 2026
28 of 34 checks passed
@KooshaPari
KooshaPari deleted the fix/rate-limiter-keyv-contract-20260805 branch August 7, 2026 06:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files typescript

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants