From 84d5337dc2821a3ed2bb229c6e3b48c3fb832997 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 6 Oct 2026 20:40:02 +0000 Subject: [PATCH 1/2] test: wake the queue after a failed boot whose destroy also fails (#400) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01KF7qvBCD86e2G7qDdeR2rC --- .../lease-acquisition-coordinator.test.ts | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/leasing/lease-acquisition-coordinator.test.ts b/src/leasing/lease-acquisition-coordinator.test.ts index 595b9357..86dd23a8 100644 --- a/src/leasing/lease-acquisition-coordinator.test.ts +++ b/src/leasing/lease-acquisition-coordinator.test.ts @@ -905,6 +905,52 @@ describe("LeaseAcquisitionCoordinator", () => { }).toEqual({ queueDepth: 0, secondGranted: true, sameDevice: false }); }); + it("grants a request queued behind a request whose shut-down device failed to boot and then to be destroyed, when another shut-down device and a running slot are free, with no release", async () => { + const harness = await createHarness({ maxDevices: 2, maxRunning: 2 }); + const held = await Promise.all([ + harness.coordinator.request(request, { ownerId: "x", requesterId: "x" }), + harness.coordinator.request(request, { ownerId: "y", requesterId: "y" }), + ]); + const failing = harness.coordinator.request(request, { ownerId: "a", requesterId: "a" }); + void failing.catch(() => undefined); + const second = harness.coordinator.request(request, { ownerId: "b", requesterId: "b" }); + const secondState = promiseState(second); + await settle(); + expect(harness.coordinator.queueDepth).toBe(2); + // Makeready calls 1 and 2 readied the held devices; call 3 is the head's boot. Its device + // then fails to be destroyed, so it stays claimed and its running slot stays reserved. + harness.driver.failOn("makeReady", 3, new DriverCrashError("simulator never booted")); + harness.driver.failOn("destroy", 1, new DriverCrashError("simulator would not die")); + // Both devices come back shut down without the release path, so only the kick below wakes + // the queue; its head is the request that fails. + for (const grant of held) { + await harness.registry.beginRelease(grant.lease.id); + await harness.registry.transitionDevice(grant.device.id, "shutdown", { + event: "device.reclaimed", + payload: { deviceId: grant.device.id, duration: 0, strategy: "wipe" }, + }); + } + harness.coordinator.kick(); + await settle(); + await expect(failing).rejects.toMatchObject({ name: "BootTimeoutError" }); + const failedDeviceId = ( + harness.driver.calls.filter((call) => call.operation === "destroy")[0]?.arguments[0] as + | { readonly deviceId: string } + | undefined + )?.deviceId; + const otherDevice = held + .map((grant) => grant.device) + .find((device) => device.driverDeviceId !== failedDeviceId); + + expect({ queueDepth: harness.coordinator.queueDepth, second: secondState.state }).toEqual({ + queueDepth: 0, + second: "fulfilled", + }); + expect(failedDeviceId).toBeDefined(); + expect(otherDevice).toBeDefined(); + expect((await second).device.id).toBe(otherDevice?.id); + }); + it("leaves a request that still cannot be served queued once, with no new work, when the device it queued behind is granted", async () => { const harness = await createHarness({ maxDevices: 1, maxRunning: 1 }); harness.driver.hangMakeReady(); From 818607b7453c6d8a80c911fc27af3074b9fe8fd6 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 6 Oct 2026 20:41:04 +0000 Subject: [PATCH 2/2] fix(leasing): wake the queue after a failed boot whether or not the destroy succeeded (#400) 1 failing -> 0 failing Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01KF7qvBCD86e2G7qDdeR2rC --- src/leasing/lease-acquisition-coordinator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/leasing/lease-acquisition-coordinator.ts b/src/leasing/lease-acquisition-coordinator.ts index 1e2814bc..7f1be997 100644 --- a/src/leasing/lease-acquisition-coordinator.ts +++ b/src/leasing/lease-acquisition-coordinator.ts @@ -908,7 +908,7 @@ export class LeaseAcquisitionCoordinator implements AcquisitionMaintenance { this.#reject(waiter, new BootTimeoutError(device.id), "boot-timeout"); } }); - if (destroyed) this.#wakeQueue(); + this.#wakeQueue(); return; } await this.#grantHandoff(waiter, handoff, "boot-shutdown", capacityReservation);