Skip to content

Drain guest responses before scale-to-zero - #369

Draft
tnsardesai wants to merge 1 commit into
mainfrom
hypeship/guest-drain-hold
Draft

Drain guest responses before scale-to-zero#369
tnsardesai wants to merge 1 commit into
mainfrom
hypeship/guest-drain-hold

Conversation

@tnsardesai

@tnsardesai tnsardesai commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

problem

An HTTP handler can return while response bytes remain unacknowledged in the guest TCP send queue. Re-enabling scale-to-zero at that point can suspend the guest before the peer receives the response tail.

change

This implements the fix entirely inside the guest image. It requires no caller or control-plane protocol changes.

  • track every non-loopback HTTP response on the API, DevTools, and ChromeDriver listeners
  • retain the request's scale-to-zero hold until the handler returns and the response reaches a safe terminal state
  • poll Linux TIOCOUTQ after net/http finalizes explicit response framing
  • use adaptive asynchronous polling so keep-alive requests do not wait on a fixed polling interval
  • cancel an older idle drain when the next request starts, carrying its hold forward until the connection next drains
  • apply rolling five-minute write deadlines so blocked writes terminate without limiting healthy long-lived streams
  • release hijacked HTTP connections from response-tail tracking while retaining the handler-lifetime hold, preserving prompt WebSocket cancellation and close behavior
  • preserve close-delimited HTTP semantics by duplicating the socket, sending FIN with shutdown(SHUT_WR), closing the Go connection promptly, and retaining the hold until TCP acknowledges the close
  • abort stalled or uninspectable sockets with SO_LINGER=0; retry abort failures with backoff while scale-to-zero remains held
  • use TCP_USER_TIMEOUT as bounded recovery if close monitoring and abort setup both fail
  • terminate failed-disable requests with http.ErrAbortHandler, preventing net/http from synthesizing a successful response for an operation that never ran
  • expose drain outcome counters plus active and fail-closed hold gauges

The release invariant is: response data and required close framing were acknowledged outside the guest, or the connection was abortively terminated. A timeout, inspection failure, or cleanup failure alone never releases the hold.

tests

  • go build ./...
  • go vet ./...
  • go test -race ./lib/scaletozero ./lib/metrics ./cmd/api/api
  • go test -race $(go list ./... | grep -v '/e2e$')
  • repeated real TCP slow-reader, chunked framing, blocked-write, timeout, and abort-recovery coverage
  • WebSocket write cancellation and CloseNow deadlines
  • server shutdown during an active idle drain
  • HTTP/1.0 close-delimited response completion and FIN acknowledgement
  • failed scale-to-zero disable plus failed abort never returning 2xx
  • keep-alive latency and drain-generation handoff
  • scale-to-zero control-file transitions during a backpressured transfer
  • Darwin amd64 and Linux arm64 compile checks for the scale-to-zero package

@tnsardesai
tnsardesai force-pushed the hypeship/guest-drain-hold branch 2 times, most recently from 831acb0 to 16df82d Compare September 6, 2026 03:37
@tnsardesai tnsardesai changed the title Wait for streaming responses to drain Drain guest responses before scale-to-zero Sep 6, 2026
@tnsardesai
tnsardesai force-pushed the hypeship/guest-drain-hold branch from 16df82d to b9e0e87 Compare September 6, 2026 16:29
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