Retry dropped AKS keepalive sockets on k8s GETs (#45)¶
Motivation¶
requests.exceptions.ConnectionError was the single highest-volume App Insights
exception (85 events / 7 days, ongoing). The AKS API server silently drops idle
pooled keepalive sockets; the next GET on that dead socket raised:
ConnectionError(ProtocolError('Connection aborted.',
RemoteDisconnected('Remote end closed connection without response')))
The warmup pod-log fan-out (api/services/k8s/warmup_status.py) already caught
the exception and degraded gracefully, but the requests OpenTelemetry
auto-instrumentation still recorded an exception row on the span for every
dropped socket, polluting telemetry on each monitor poll tick.
Root cause¶
The pooled k8s session's urllib3.Retry (_build_k8s_retry) used read=0.
urllib3 classifies a RemoteDisconnected / ProtocolError as a read error,
so read=0 made the very first abort terminal — the request was never retried
even though an immediate reconnect would have succeeded.
User-facing change¶
None visible. The warmup status card behaves identically (it already fell back to "logs unavailable" on failure). The change is that a dropped-keepalive abort is now transparently retried once instead of surfacing as a recorded exception.
API / IaC diff summary¶
api/services/k8s/client.py—_build_k8s_retry()now setsread=_k8s_session_retry_total()(was0). The single retry budget (total=1) bounds it, andallowed_methodsstays GET/HEAD/OPTIONS only, so a mutating POST/PATCH/DELETE is still never replayed. HTTP status codes remain non-retried (status=0,status_forcelist=()).api/tests/test_k8s_retry.py— updated the pinned-config assertion (read == _K8S_SESSION_RETRY_TOTAL) and added two regression tests:- a
RemoteDisconnected/ProtocolErroron an idempotent GET is retried once then bounded (second abort raisesMaxRetryError); - the same abort on a POST is not retried (urllib3 re-raises the original
ProtocolError).