diff --git a/benchmarking/workloads/manifests/workloads.yaml.tmpl b/benchmarking/workloads/manifests/workloads.yaml.tmpl index 5fa120487..84f2c062f 100644 --- a/benchmarking/workloads/manifests/workloads.yaml.tmpl +++ b/benchmarking/workloads/manifests/workloads.yaml.tmpl @@ -70,6 +70,15 @@ spec: - "--metrics-listen-addr=:9090" - "--data-dir=/tmp/glutton" - "--mode=http" + # Hold ResumeActor open until port 80 actually answers. Starting the + # process is not the same as it accepting connections, and without this + # gate a ping can arrive in the gap and be refused. Probing port 80 rather + # than the metrics port is deliberate: it is the port the router forwards + # to. + readyz: + httpGet: + path: /readyz + port: 80 workerSelector: matchLabels: workload: benchmark-ateom diff --git a/cmd/benchmarking/glutton/main.go b/cmd/benchmarking/glutton/main.go index 90079bb81..085e92c0e 100644 --- a/cmd/benchmarking/glutton/main.go +++ b/cmd/benchmarking/glutton/main.go @@ -136,6 +136,18 @@ func main() { // (re-exposable as additional routes if/when needed). mux := http.NewServeMux() mux.HandleFunc("/ping", httpPingHandler(svc)) + // Readiness gate for the actor lifecycle. ateom blocks RestoreWorkload + // on this returning 200 (internal/readyz), so ResumeActor does not + // report success until the socket the router forwards to is genuinely + // serving. Without it a ping can beat the listener and get + // ECONNREFUSED, which the router turns into a 503. + // + // It must live on this listener rather than the metrics port: the + // metrics server starts independently, so a 200 there would say + // nothing about whether /ping is reachable yet. + mux.HandleFunc("/readyz", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + }) // otelhttp at the mux level + per-handler span follows // docs/dev/best-practices/tracing.md: extract incoming context, // then name the span after the operation in each handler.