Skip to content

Decouple router in-flight counter from queued requests - #143

Open
scott-rc wants to merge 1 commit into
mainfrom
sc/fix-cold-start-feedback
Open

scott-rc wants to merge 1 commit into
mainfrom
sc/fix-cold-start-feedback

Conversation

@scott-rc

Copy link
Copy Markdown
Contributor

Why

The router's requests_in_flight counter incremented at the start of ServeHTTP, before ctrl.Instance() had returned an instance. Requests waiting for assignment counted as serving load. When controller-side assignment slowed (e.g. cohort transition during a deploy), queue-inflated heartbeats reported false demand, the controller scaled up further, and the resulting contention slowed assignment more -- a positive feedback loop that turned isolated stalls into a cluster-wide 5xx storm.

What

The counter now reflects only requests being proxied to an instance. An inFlightTracker plumbed via context marks the request once Instance() succeeds and is released in ServeHTTP's defer. Mark and release are idempotent (atomic CAS) so retries do not double-count. The requests_in_flight metric's Help string is updated to match.

What this does NOT fix

The underlying bottleneck -- multiple controllers racing on K8s JSON-patch operations against the shared unassigned-pod pool, which stalls GetInstance calls under contention -- remains. This change removes the amplifier that turns isolated stalls into cluster-wide events; a follow-up branch will address the contention directly.

Tests

  • TestHeartbeatInFlightExcludesRequestsWaitingForInstance pins the queued-not-served invariant.
  • TestHeartbeatInFlightIdempotentAcrossRetries covers the CAS guard across Instance() retries.
  • TestHeartbeatStateSurvivesLongInFlightRequest exercises the per-request touch goroutine across the heartbeat GC threshold.

A request entering the router incremented state.inFlight before ctrl.Instance()
returned, so requests waiting for an instance counted as serving load. Under
slow controller-side assignment, queue-inflated heartbeats reported false
demand, the controller scaled up further, and the resulting contention slowed
assignment more -- a feedback loop that turned a small slow-path event into a
cluster-wide 5xx storm.

The counter now reflects only requests being proxied to an instance, removing
the loop's amplifier. An inFlightTracker plumbed via context idempotently
marks the request once Instance() succeeds and is released in ServeHTTP's
defer. The requests_in_flight metric's Help string is updated to match. Tests
cover the queued-request invariant, retry idempotency, and the per-request
touch goroutine's keepalive role across the heartbeat GC threshold.
@scott-rc

Copy link
Copy Markdown
Contributor Author

This change is part of the following stack:

Change managed by git-spice.

This branch has not been deployed

No deployments
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.

1 participant