Skip to content

orchestrator-kubernetes: Fix replica metrics fetches failing on idle connections - #39696

Merged
antiguru merged 1 commit into
MaterializeInc:mainfrom
antiguru:moritz/cpu-306-kube-read-timeout
Oct 9, 2026
Merged

antiguru merged 1 commit into
MaterializeInc:mainfrom
antiguru:moritz/cpu-306-kube-read-timeout

Conversation

@antiguru

@antiguru antiguru commented Oct 9, 2026 •

Copy link
Copy Markdown
Member

For multi-process replicas, the per-minute metrics fetch fails for processes 1+ with client error (SendRequest), leaving their rows in mz_cluster_replica_metrics_history NULL. hyper-timeout runs the kube client's read timer while a pooled connection sits idle and does not reset it on write. The extra connections for processes 1+ come back after about 60 s idle, so the 60 s read timeout fires before the response arrives. This raises the read timeout to 120 s, above the pool's 90 s idle expiry, with a TODO to return to 60 s once kube-client enables reset_reader_on_write.

It also adds mz_orchestrator_kubernetes_process_metrics_fetch_failures_total, labeled by fetch step and error kind, and logs the full error chain, which previously stopped at SendRequest. Unit tests cover the error classification in src/orchestrator-kubernetes/src/metrics/tests.rs.

Raising the timeout doubles how long a hung Kubernetes call can block an orchestrator worker. CPU-306 found no orchestrator call hitting the timeout in recent production logs or Kubernetes-based CI.

Closes: CPU-306

🤖 Generated with Claude Code

…connections

The Kubernetes client's 60 s read timeout fires on pooled connections that
idle for close to 60 s, because hyper-timeout runs the read timer while a
connection sits idle and does not reset it when a request is written. The
per-minute replica metrics fetch reuses such connections for processes 1+
and fails with `client error (SendRequest)`, leaving their metrics NULL.
Raise the read timeout to 120 s, above the pool's 90 s idle expiry.

Count failed per-process metrics fetches in
`mz_orchestrator_kubernetes_process_metrics_fetch_failures_total`, labeled
by fetch step and error kind, and log the full error chain.

Closes: CPU-306

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@antiguru
antiguru requested review from a team as code owners October 9, 2026 11:26
@ggevay
ggevay self-requested a review October 9, 2026 11:52

@ggevay ggevay left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks! Minor comments.

The branch name contains cpu-306, so merging auto-closes CPU-306. Its upstream half (getting back to 60 s) stays open, though, and TODO(CPU-306) would then point at a closed issue. Maybe keep CPU-306 open, or point the TODO at the kube-rs PR instead.

// therefore times out before the response arrives and fails with
// `client error (SendRequest)`.
//
// TODO(CPU-306): Return to 60 s once kube-client enables hyper-timeout's

@ggevay ggevay Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kube-client won't enable it by default: the upstream change (kube-rs#2111) exposes it as an opt-in Config::reset_reader_on_write, and it can only ship in a kube-client release after 4.2.0 (we're on 3.1.0). Suggest: "TODO: set reset_reader_on_write and return to 60 s once we're on a kube-client release with kube-rs#2111."

// The read timeout bounds how long a hung Kubernetes call can block an orchestrator worker,
// which handles one command at a time.
//
// NOTE: The read timeout must exceed the connection pool's 90 s idle expiry. hyper-timeout

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: exceeding 90 s isn't quite sufficient. Pool idle time counts against the next response for any read timeout, so at 120 s a request on a connection that idled ~89 s has ~31 s left. That's harmless for these millisecond-scale requests, but the actual constraint is "90 s plus the slowest response".

@antiguru
antiguru merged commit 0009374 into MaterializeInc:main Oct 9, 2026
91 checks passed

antiguru commented Oct 9, 2026

Copy link
Copy Markdown
Member Author

I re-opened CPU-306 after merging the PR. Thanks for the reviews!

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.

3 participants