From ac071b13e0b215ef6cbf16ab26ce2e326c3c620d Mon Sep 17 00:00:00 2001 From: Jakub Dobry Date: Tue, 14 Apr 2026 00:44:31 -0700 Subject: [PATCH 1/3] fix(orchestrator): prefer running sandbox on host IP lookups --- packages/orchestrator/pkg/sandbox/map.go | 18 +++++- packages/orchestrator/pkg/sandbox/map_test.go | 58 +++++++++++++++++++ 2 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 packages/orchestrator/pkg/sandbox/map_test.go diff --git a/packages/orchestrator/pkg/sandbox/map.go b/packages/orchestrator/pkg/sandbox/map.go index 13430d637d..91c99eba75 100644 --- a/packages/orchestrator/pkg/sandbox/map.go +++ b/packages/orchestrator/pkg/sandbox/map.go @@ -88,17 +88,31 @@ func (m *Map) Get(sandboxID string) (*Sandbox, bool) { } // GetByHostPort looks up a sandbox by its host IP address parsed from hostPort. -// It matches any sandbox in the map (starting, running, or stopping). +// It prefers a running sandbox and only falls back to a non-running one when +// no running sandbox matches. func (m *Map) GetByHostPort(hostPort string) (*Sandbox, error) { reqIP, _, err := net.SplitHostPort(hostPort) if err != nil { return nil, fmt.Errorf("error parsing remote address %s: %w", hostPort, err) } + var fallback *Sandbox for _, sbx := range m.sandboxes.Items() { - if sbx.Slot.HostIPString() == reqIP { + if sbx.Slot.HostIPString() != reqIP { + continue + } + + if sbx.IsRunning() { return sbx, nil } + + if fallback == nil { + fallback = sbx + } + } + + if fallback != nil { + return fallback, nil } return nil, fmt.Errorf("sandbox with address %s not found", hostPort) diff --git a/packages/orchestrator/pkg/sandbox/map_test.go b/packages/orchestrator/pkg/sandbox/map_test.go new file mode 100644 index 0000000000..40849f4198 --- /dev/null +++ b/packages/orchestrator/pkg/sandbox/map_test.go @@ -0,0 +1,58 @@ +package sandbox + +import ( + "net" + "testing" + + "github.com/e2b-dev/infra/packages/orchestrator/pkg/sandbox/network" +) + +func TestGetByHostPortPrefersRunningSandbox(t *testing.T) { + m := NewSandboxesMap() + + stopping := &Sandbox{ + Resources: &Resources{ + Slot: &network.Slot{HostIP: net.ParseIP("10.11.0.2")}, + }, + } + stopping.status.Store(int32(StatusStopping)) + m.sandboxes.Insert("stopping", stopping) + + running := &Sandbox{ + Resources: &Resources{ + Slot: &network.Slot{HostIP: net.ParseIP("10.11.0.2")}, + }, + } + running.status.Store(int32(StatusRunning)) + m.sandboxes.Insert("running", running) + + sbx, err := m.GetByHostPort("10.11.0.2:2049") + if err != nil { + t.Fatalf("GetByHostPort returned error: %v", err) + } + + if sbx != running { + t.Fatalf("expected running sandbox, got %#v", sbx) + } +} + +func TestGetByHostPortFallsBackToStoppingSandbox(t *testing.T) { + m := NewSandboxesMap() + + stopping := &Sandbox{ + Resources: &Resources{ + Slot: &network.Slot{HostIP: net.ParseIP("10.11.0.3")}, + }, + } + stopping.status.Store(int32(StatusStopping)) + m.sandboxes.Insert("stopping", stopping) + + sbx, err := m.GetByHostPort("10.11.0.3:2049") + if err != nil { + t.Fatalf("GetByHostPort returned error: %v", err) + } + + if sbx != stopping { + t.Fatalf("expected stopping sandbox, got %#v", sbx) + } +} From 2601699e8516ecbbfd4244d590514b11cdd6f20c Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 14 Apr 2026 22:32:58 +0000 Subject: [PATCH 2/3] fix(orchestrator): use testify/require in tests and prefer starting over stopping in fallback - Switch tests to use require.NoError/require.Same to match repo conventions - In GetByHostPort fallback, prefer StatusStarting over StatusStopping so that when an IP slot is freed and reused, we route to the new sandbox - Add TestGetByHostPortPrefersStartingOverStopping test Co-Authored-By: jakub.dobry --- packages/orchestrator/pkg/sandbox/map.go | 5 ++- packages/orchestrator/pkg/sandbox/map_test.go | 38 +++++++++++++------ 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/packages/orchestrator/pkg/sandbox/map.go b/packages/orchestrator/pkg/sandbox/map.go index 91c99eba75..12e65be690 100644 --- a/packages/orchestrator/pkg/sandbox/map.go +++ b/packages/orchestrator/pkg/sandbox/map.go @@ -106,7 +106,10 @@ func (m *Map) GetByHostPort(hostPort string) (*Sandbox, error) { return sbx, nil } - if fallback == nil { + // Prefer a starting sandbox over a stopping one so that when an IP + // slot is freed by a stopping sandbox and immediately reused by a + // new starting sandbox, we route to the new sandbox. + if fallback == nil || SandboxStatus(sbx.status.Load()) == StatusStarting { fallback = sbx } } diff --git a/packages/orchestrator/pkg/sandbox/map_test.go b/packages/orchestrator/pkg/sandbox/map_test.go index 40849f4198..96d5ce194c 100644 --- a/packages/orchestrator/pkg/sandbox/map_test.go +++ b/packages/orchestrator/pkg/sandbox/map_test.go @@ -4,6 +4,8 @@ import ( "net" "testing" + "github.com/stretchr/testify/require" + "github.com/e2b-dev/infra/packages/orchestrator/pkg/sandbox/network" ) @@ -27,13 +29,32 @@ func TestGetByHostPortPrefersRunningSandbox(t *testing.T) { m.sandboxes.Insert("running", running) sbx, err := m.GetByHostPort("10.11.0.2:2049") - if err != nil { - t.Fatalf("GetByHostPort returned error: %v", err) + require.NoError(t, err) + require.Same(t, running, sbx) +} + +func TestGetByHostPortPrefersStartingOverStopping(t *testing.T) { + m := NewSandboxesMap() + + stopping := &Sandbox{ + Resources: &Resources{ + Slot: &network.Slot{HostIP: net.ParseIP("10.11.0.2")}, + }, } + stopping.status.Store(int32(StatusStopping)) + m.sandboxes.Insert("stopping", stopping) - if sbx != running { - t.Fatalf("expected running sandbox, got %#v", sbx) + starting := &Sandbox{ + Resources: &Resources{ + Slot: &network.Slot{HostIP: net.ParseIP("10.11.0.2")}, + }, } + starting.status.Store(int32(StatusStarting)) + m.sandboxes.Insert("starting", starting) + + sbx, err := m.GetByHostPort("10.11.0.2:2049") + require.NoError(t, err) + require.Same(t, starting, sbx) } func TestGetByHostPortFallsBackToStoppingSandbox(t *testing.T) { @@ -48,11 +69,6 @@ func TestGetByHostPortFallsBackToStoppingSandbox(t *testing.T) { m.sandboxes.Insert("stopping", stopping) sbx, err := m.GetByHostPort("10.11.0.3:2049") - if err != nil { - t.Fatalf("GetByHostPort returned error: %v", err) - } - - if sbx != stopping { - t.Fatalf("expected stopping sandbox, got %#v", sbx) - } + require.NoError(t, err) + require.Same(t, stopping, sbx) } From f4db401f764d5313c2d3c0a8864636cd98d804d9 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 14 Apr 2026 22:36:48 +0000 Subject: [PATCH 3/3] fix(orchestrator): add t.Parallel() to satisfy paralleltest linter Co-Authored-By: jakub.dobry --- packages/orchestrator/pkg/sandbox/map_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/orchestrator/pkg/sandbox/map_test.go b/packages/orchestrator/pkg/sandbox/map_test.go index 96d5ce194c..0d698a054c 100644 --- a/packages/orchestrator/pkg/sandbox/map_test.go +++ b/packages/orchestrator/pkg/sandbox/map_test.go @@ -10,6 +10,8 @@ import ( ) func TestGetByHostPortPrefersRunningSandbox(t *testing.T) { + t.Parallel() + m := NewSandboxesMap() stopping := &Sandbox{ @@ -34,6 +36,8 @@ func TestGetByHostPortPrefersRunningSandbox(t *testing.T) { } func TestGetByHostPortPrefersStartingOverStopping(t *testing.T) { + t.Parallel() + m := NewSandboxesMap() stopping := &Sandbox{ @@ -58,6 +62,8 @@ func TestGetByHostPortPrefersStartingOverStopping(t *testing.T) { } func TestGetByHostPortFallsBackToStoppingSandbox(t *testing.T) { + t.Parallel() + m := NewSandboxesMap() stopping := &Sandbox{