Repository navigation
feat(control-plane): add per-key rate limiting on hot endpoints - #952
Merged
santoshkumarradha merged 3 commits intoAug 24, 2026
Merged
santoshkumarradha merged 3 commits into
santoshkumarradha merged 3 commits into
Conversation
Contributor
📊 Coverage gateThresholds from
✅ Gate passedNo surface regressed past the allowed threshold and the aggregate stayed above the floor. |
Contributor
📐 Patch coverage gateThreshold: 80% on lines this PR touches vs
✅ Patch gate passedEvery surface whose lines were touched by this PR has patch coverage at or above the threshold. |
…t-Field#429) Without rate limiting, a single misbehaving client can exhaust the async worker pool and database connections on hot endpoints, starving legitimate traffic. Implementation: - Add RateLimiterStore using golang.org/x/time/rate with per-key token-bucket limiters and background idle eviction (10 min TTL). - Add RateLimit gin middleware that keys on API key (from auth middleware) or client IP, returns HTTP 429 with Retry-After header. - Add RateLimitConfig to agentfield.yaml config with per-endpoint settings: execute (50 rps/100 burst), discovery (20/40), bulk_status (30/60), global (200/400). - Wire into routes: global limiter on /api/v1, dedicated limiters on /execute group, /discovery group, and /nodes/status/bulk. - Opt-in via config: rate_limit.enabled = true in agentfield.yaml. Disabled by default to avoid breaking existing deployments. - Graceful shutdown: Stop() halts eviction goroutines. Tests (9 cases): - Token bucket enforcement and burst - Idle key eviction - Active key not evicted - Normal traffic allowed - 429 returned with Retry-After header and JSON body - Per-key isolation (different clients independent) - API-key-based keying when auth is present - Config enabled/disabled logic - Default config values
7vignesh
force-pushed
the
feat/429-rate-limiting-hot-endpoints
branch
from
August 23, 2026 19:11
ef5429f to
1043654
Compare
santoshkumarradha
approved these changes
Aug 24, 2026
santoshkumarradha
left a comment
Member
There was a problem hiding this comment.
Checked the new rate-limit wiring locally. The endpoint-specific tests pass, the feature stays inert unless explicitly enabled, and the config surface is straightforward.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #429. Adds per-key token-bucket rate limiting to the control plane's hot endpoints (
/execute,/discovery,/nodes/status/bulk), preventing a single misbehaving client from exhausting the async worker pool and database connections.Type of change
Implementation
Middleware (
middleware/ratelimit.go)RateLimiterStore— per-keygolang.org/x/time/ratetoken-bucket limiters with async.Mutex-guarded map and background eviction of idle keys (10 min TTL)RateLimitgin middleware — extracts key from API key identity (set by auth middleware) or falls back to client IP; returns HTTP 429 withRetry-Afterheader and JSON body on limit breachRateLimitConfig/RateLimitsConfig— per-endpoint configuration types withIsEnabled()andDefaultRateLimitConfig()Config (
config/config.go)RateLimitConfigtoAgentFieldConfig— configurable viaagentfield.yaml:Routes (
routes_core.go,server.go)initRateLimiters()— creates per-endpoint stores from config at startup/api/v1(global),/executegroup,/discoverygroup,/nodes/status/bulkStop()halts eviction goroutinesrate_limit.enabled: false) to avoid breaking existing deploymentsDefaults (when enabled)
Test plan
cd control-plane && go test ./internal/server/middleware/ -run TestRateLimit -v(9 tests)cd control-plane && go test ./internal/config/ -timeout 30scd control-plane && go build ./...cd control-plane && go vet ./internal/server/... ./internal/config/...cd control-plane && golangci-lint run ./internal/server/middleware/(no new warnings)Test coverage
coverage-baseline.jsonin this PR only if the removal caused a legitimate regression.Checklist
Related issues / PRs
Fixes #429
Notes
TestGinLoggerEmitsStructuredDebugLineis a pre-existing flaky test unrelated to this change.golang.org/x/timewas already an indirect dependency (v0.8.0 via otel); upgraded to v0.15.0.