Add checkin capacity-rejection rate metric to /stats - #7330
Merged
ycombinator merged 2 commits intoJul 9, 2026
Conversation
Expose http_server.routes.checkin.limit_max_rate, a server-computed rolling rate (rejections/sec) of the existing limit_max counter, i.e. checkins rejected because Fleet Server was at capacity (the per-route concurrency limit was reached). Unlike checkin volume, limit_max can only be incremented by Fleet Server itself rejecting a request, so it can't be inflated by a client checking in unusually often (or a misbehaving agent). This makes the rate a safer signal for downstream autoscaling decisions than raw checkin throughput would be. The rate is sampled on a 10s interval by a new background subsystem, RunCheckinRejectionRateSampler, following the same errgroup lifecycle as the other subsystems in Fleet.runSubsystems. Relates elastic/ingest-dev#7719. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Contributor
|
This pull request does not have a backport label. Could you fix it @ycombinator? 🙏
|
ycombinator
requested review from
a team,
cmacknz,
lorienhu and
macdewee
and removed request for
a team
July 8, 2026 00:10
- Expand computeRate's doc comment to explain the +0.5 rounding trick and correct an inaccurate claim that a process restart can cause cur < prev (it can't: prev is re-read from the same live counter on restart, so both start back at 0 together). - Replace the manual poll loop in TestRunCheckinRejectionRateSampler with require.Eventually, and move goroutine teardown into t.Cleanup so it still runs if the Eventually check fails. - Use require.* consistently instead of assert.* in this test file. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
ycombinator
marked this pull request as ready for review
July 8, 2026 00:38
Contributor
Author
|
@cavokz I am unable to add you as a reviewer on this PR but would like your input as well. If not specifically on the implementation then at least on the idea behind this enhancement. Thank you! |
samuelvl
approved these changes
Jul 8, 2026
Contributor
|
Tick the box to add this pull request to the merge queue (same as
|
cmacknz
approved these changes
Jul 8, 2026
Member
|
Nice, will be very curious to see how well this works once wired up to the autoscaler. |
cavokz
approved these changes
Jul 9, 2026
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.
What is the problem this PR solves?
The EPA autoscaler for Fleet Server (configured in fleet-controller) currently scales on
http_server.tcp_active(active TCP connections). This has repeatedly failed to scale up pods fast enough under load in Serverless scale tests — including the 20k → 30k drone check-in test tracked in elastic/ingest-dev#7719, and other runs such as a recent 5k test where the autoscaler did not add pods when it should have.The root cause: when a pod is saturated, agents being turned away don't count as "active" connections, so
tcp_activeunderstates real demand and the autoscaler under-reacts.In this comment, @cmacknz suggested scaling on the rate of check-ins rejected because Fleet Server was at capacity instead — a server-side saturation signal that (unlike checkin volume) can't be inflated by a valid-but-misbehaving or malicious client.
Fleet Server already counts these rejections (
http_server.routes.checkin.limit_max), but it's a monotonically increasing counter, not a rate — not directly usable as an autoscaler trigger (the EPA reads metric values as levels, not deltas).How does this PR solve the problem?
Adds a new gauge,
http_server.routes.checkin.limit_max_rate, to the internal/statsendpoint. A new background subsystem (api.RunCheckinRejectionRateSampler, wired intoFleet.runSubsystemsalongside the other subsystems) samples the existinglimit_maxcounter every 10s and publishes the delta as a rejections/sec rate.This is intentionally computed inside Fleet Server, not left to the consumer (e.g. an autoscaler) to derive from the raw counter, so the rate is available directly via
/statsin a form ready to use as a metric target.This PR only adds the metric; it does not change any existing behavior or defaults. A follow-up draft PR to fleet-controller (elastic/fleet-controller#623) wires the EPA to this new metric as an additional (not replacing) autoscaling trigger.
/statsresponse — a missing metric aborts that reconcile cycle entirely (including evaluation of the existingtcp_activetrigger), rather than degrading gracefully. See the fleet-controller PR for how this is gated (metric is opt-in via config, defaulted off).How to test this PR locally
http.enabled: true, default port5066).curl localhost:5066/stats | jq .http_server.routes.checkin.limit_max_rate— should read0at idle.checkin_limit.max(e.g. via a load test) and confirm the value rises, then falls back to0once rejections stop.go test ./internal/pkg/api/... -run TestComputeRateand-run TestRunCheckinRejectionRateSamplerfor the unit-level behavior, andgo test -tags integration ./internal/pkg/api/... -run TestMetricsEndpointsfor the/statsJSON shape.Design Checklist
Checklist
./changelog/fragmentsusing the changelog toolRelated issues