Skip to content

Comments

feat: workspace ratelimits#5057

Open
Flo4604 wants to merge 25 commits intomainfrom
eng-2427-add-billing-quota-and-per-region-rate-limiting
Open

feat: workspace ratelimits#5057
Flo4604 wants to merge 25 commits intomainfrom
eng-2427-add-billing-quota-and-per-region-rate-limiting

Conversation

@Flo4604
Copy link
Member

@Flo4604 Flo4604 commented Feb 16, 2026

What does this PR do?

Fixes #4507
FIxes #4850

This well adds ratelimiting workspace wide for all of our API routes using a single general limit basically rps per workspace.

in the quotas table both the limit and duration can be overriden, null being unlimited and 0 nothing and everything above well yeah.

This refactors our the ratelimit namespace creation into a seperate internal service as this is somewhat shared so we can raw-dog our own analytics in the unkey workspace that also hosts the rootkeys. It will own the analytics.

If we wanted we could also make overrides work? and just use that but the db does work nicely here.

  • Adds a new singeflight pkg to atleast limit concurrency when creating ratelimit namespaces on concurrent requests to the same node.
  • Adds 429 errors to openapi spec for all endpoints.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • Chore (refactoring code, technical debt, workflow improvements)
  • Enhancement (small improvements)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How should this be tested?

The tests should cover it, otherwise:

  • make dev

  • run UPDATE unkey.quota t SET t.ratelimit_api_limit = 1, t.ratelimit_api_duration = 60000 WHERE t.slug = 'ws_local' e.g

  • Call the api multiple times

curl http://127.0.0.1:7070/v2/keys.getKey \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer <ROOT_KEY>' \
  --data '{  "keyId": "your_key" }' | jq

Api calls should fail with 429 once exceeded.

Checklist

Required

  • Filled out the "How to test" section in this PR
  • Read Contributing Guide
  • Self-reviewed my own code
  • Commented on my code in hard-to-understand areas
  • Ran pnpm build
  • Ran pnpm fmt
  • Ran make fmt on /go directory
  • Checked for warnings, there are none
  • Removed all console.logs
  • Merged the latest changes from main onto my branch with git pull origin main
  • My changes don't cause any responsiveness issues

Appreciated

  • If a UI change was made: Added a screen recording or screenshots to this PR
  • Updated the Unkey Docs if changes were necessary

@vercel
Copy link

vercel bot commented Feb 16, 2026

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

Project Deployment Actions Updated (UTC)
dashboard Ready Ready Preview, Comment Feb 19, 2026 5:38pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
engineering Ignored Ignored Preview Feb 19, 2026 5:38pm

Request Review

@linear
Copy link

linear bot commented Feb 16, 2026

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 16, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds workspace-level API rate limiting: DB/schema and model additions for quota ratelimit fields, a new ratelimit namespace service (create/get/invalidate) with caching and singleflight, quota cache wiring, keys service workspace-rate checks, refactors ratelimit route handlers to use Namespaces, and OpenAPI/error mappings for 429 responses.

Changes

Cohort / File(s) Summary
Ratelimit namespace service
internal/services/ratelimit/namespace/..., internal/services/ratelimit/namespace/BUILD.bazel
New namespace.Service: interface, constructor, Create/CreateMany/Get/GetMany/Invalidate, SWR caching, singleflight create, audit logging, unit tests, and BUILD rule.
Keys service & workspace rate-limit
internal/services/keys/service.go, internal/services/keys/workspace_ratelimit.go, internal/services/keys/get.go, internal/services/keys/BUILD.bazel
Added Config.QuotaCache and service.quotaCache; new checkWorkspaceRateLimit method; integrated workspace rate-limit check into GetRootKey flow and wiring.
Quota DB, models & codegen
pkg/db/schema.sql, pkg/db/models_generated.go, pkg/db/*quota*.sql_generated.go, pkg/db/queries/quota_upsert.sql, pkg/db/sqlc.json
Added ratelimit_api_limit and ratelimit_api_duration columns; renamed quota type to Quotas, added nullable ratelimit fields, updated generated queries, upsert params, and sqlc rename mapping.
Caches wiring
internal/services/caches/caches.go
Added WorkspaceQuota cache (string -> db.Quotas) with config and tracing; wired into Caches and error propagation.
Route handlers refactor
svc/api/routes/*v2_ratelimit*/*, svc/api/routes/register.go, svc/api/routes/services.go
Replaced direct DB/cache usage with centralized namespace.Service across ratelimit handlers (limit, multiLimit, set/get/deleteOverride); updated Handler structs, DI, permission checks, and transaction flows to use Namespaces.
Run & DI + test harness
svc/api/run.go, svc/api/internal/testutil/http.go, svc/api/BUILD.bazel, svc/api/routes/BUILD.bazel
Constructed namespace service and passed into routes and keys; test harness updated to init/expose Namespaces; BUILD deps adjusted.
OpenAPI & error mapping
svc/api/openapi/openapi-generated.yaml, svc/api/openapi/spec/paths/**
Added TooManyRequestsErrorResponse and added 429 responses across many endpoints; middleware maps new workspace_rate_limited code to HTTP 429.
Error codes
pkg/codes/constants_gen.go, pkg/codes/user_request.go
Added user error code workspace_rate_limited and populated it in TooManyRequests codes.
Seed, tests & integration
cmd/dev/seed/*, svc/ctrl/integration/seed/seed.go, svc/api/routes/*, svc/api/routes/.../429_test.go
Seed and tests updated to set RatelimitApiLimit/RatelimitApiDuration in upserts; added 429-focused tests for getOverride and adjusted many tests/handlers to use Namespaces.
Supporting utilities
pkg/singleflight/singleflight.go, pkg/singleflight/BUILD.bazel
Added generic type-safe singleflight Group[T] wrapper and BUILD rule.
Web & docs
web/apps/docs/docs.json, web/apps/dashboard/lib/quotas.ts, web/internal/db/src/schema/quota.ts
Docs page for workspace_rate_limited added; dashboard quota object and web DB schema extended with ratelimit fields.
Misc / build updates
various BUILD.bazel, small files
Multiple BUILD dependency updates to add/remove internal/services/ratelimit/namespace; updated handlers and tests to the new Namespaces surface; small refactors across codebase to use Quotas.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Client
    participant KeysSvc as KeysService
    participant QuotaCache as WorkspaceQuotaCache
    participant DB as Database
    participant NamespaceSvc as NamespaceService
    participant Ratelimiter as Ratelimiter

    Client->>KeysSvc: GetRootKey(req)
    KeysSvc->>QuotaCache: SWR Get(workspaceID)
    alt cache hit
        QuotaCache-->>KeysSvc: quota
    else cache miss
        QuotaCache->>DB: FindQuotaByWorkspaceID(workspaceID)
        DB-->>QuotaCache: quota
        QuotaCache-->>KeysSvc: quota
    end

    KeysSvc->>KeysSvc: checkWorkspaceRateLimit(ctx, quota)
    KeysSvc->>NamespaceSvc: Get/Maybe Create(namespaceName)
    NamespaceSvc->>DB: Get/Create namespace row
    DB-->>NamespaceSvc: namespace
    NamespaceSvc-->>KeysSvc: namespace

    KeysSvc->>Ratelimiter: Ratelimit(namespace.ID, limit, duration)
    alt allowed
        Ratelimiter-->>KeysSvc: allowed
        KeysSvc-->>Client: 200 OK
    else denied
        Ratelimiter-->>KeysSvc: denied
        KeysSvc-->>Client: 429 Too Many Requests
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.91% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Out of Scope Changes check ❓ Inconclusive While most changes align with workspace rate limiting, the extensive OpenAPI spec updates (280+ lines) across 40+ endpoint files appear to be documentation rather than functional code, making it unclear if they are core to the objective. Clarify whether the bulk OpenAPI spec changes are essential or could be deferred to improve focus; consider if they add necessary test coverage for the rate-limit feature.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title "feat: workspace ratelimits" clearly summarizes the main feature addition: implementing workspace-wide rate limiting for API routes.
Description check ✅ Passed The PR description covers the main objectives, testing instructions, and references the linked issues, though some checklist items remain unchecked.
Linked Issues check ✅ Passed The PR successfully addresses both #4507 (fixes transactional error handling in v2_ratelimit_limit) and #4850 (implements workspace-wide rate limiting with quota persistence).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch eng-2427-add-billing-quota-and-per-region-rate-limiting

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

Copy link
Member Author

Flo4604 commented Feb 16, 2026

This stack of pull requests is managed by Graphite. Learn more about stacking.

@Flo4604 Flo4604 changed the title init changes feat: workspace ratelimits Feb 16, 2026
@Flo4604 Flo4604 force-pushed the eng-2427-add-billing-quota-and-per-region-rate-limiting branch from 65378db to 1a41386 Compare February 17, 2026 19:13
@Flo4604 Flo4604 marked this pull request as ready for review February 17, 2026 19:14
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.

Add billing quota and per-region rate limiting Fix broken error handling in v2_ratelimit_limit handler

2 participants