From d59d828cafebd73c65cf8ea7d2162830bb8aec57 Mon Sep 17 00:00:00 2001 From: AdaAibaby Date: Mon, 31 Aug 2026 14:56:31 +0800 Subject: [PATCH] fix(envd): bind HTTP listener before sync init chain to eliminate startup race MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move net.Listen to the top of run() so port :49983 is reachable from process start. Incoming connections are queued in the kernel accept backlog and dispatched once s.Serve(ln) begins — no change in HTTP semantics. Previously s.ListenAndServe() was called last, after a long synchronous chain (logger → filesystem service → cgroup manager → process service → api → port scanner/forwarder). Any blocking call in that chain — most plausibly writeCgroupProp writing memory.high/memory.max under host memory pressure — left the port returning ECONNREFUSED for the entire init duration while the process appeared alive. Orchestrators and health checks that TCP-dial :49983 could not distinguish this from a crash. Also add stderr phase logs around createCgroupManager (the most likely blocking point) so a hung init surfaces in logs rather than a silent stall. --- packages/envd/main.go | 19 ++++++++++++++++++- packages/envd/pkg/version.go | 2 +- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/envd/main.go b/packages/envd/main.go index 0dd08d211c..1777b082c8 100644 --- a/packages/envd/main.go +++ b/packages/envd/main.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "log" + "net" "net/http" "os" "path/filepath" @@ -179,6 +180,20 @@ func run() error { ctx, cancel := context.WithCancel(context.Background()) defer cancel() + // Bind the listener before any blocking initializer so the TCP port is + // reachable from process start. Incoming connections are queued in the + // kernel's accept backlog; nothing is dispatched until s.Serve(ln) below. + // Without this, a slow or hanging initializer (e.g. writeCgroupProp under + // memory pressure) produces a window where the process is alive but the port + // returns ECONNREFUSED — indistinguishable from a crashed envd to any + // TCP-based health check or orchestrator readiness probe. + ln, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", port)) + if err != nil { + return fmt.Errorf("bind envd listener: %w", err) + } + defer ln.Close() + fmt.Fprintf(os.Stderr, "envd: HTTP listener bound on :%d\n", port) + if err := os.MkdirAll(host.E2BRunDir, 0o755); err != nil { fmt.Fprintf(os.Stderr, "error creating E2B run directory: %v\n", err) } @@ -207,7 +222,9 @@ func run() error { fsLogger := l.With().Str("logger", "filesystem").Logger() filesystemService := filesystemRpc.Handle(m, &fsLogger, defaults) + fmt.Fprintf(os.Stderr, "envd startup: creating cgroup manager\n") cgroupManager := createCgroupManager() + fmt.Fprintf(os.Stderr, "envd startup: cgroup manager ready\n") defer func() { err := cgroupManager.Close() if err != nil { @@ -445,7 +462,7 @@ func run() error { } }) - err := s.ListenAndServe() + err = s.Serve(ln) // Signal goroutines to stop before deferred cleanup closes their resources. // TODO: shutdown synchronization needs to be revisited. cancel() diff --git a/packages/envd/pkg/version.go b/packages/envd/pkg/version.go index cc6c4dea69..e4b403a71c 100644 --- a/packages/envd/pkg/version.go +++ b/packages/envd/pkg/version.go @@ -1,3 +1,3 @@ package pkg -const Version = "0.7.0" // x-release-please-version +const Version = "0.7.1" // x-release-please-version