fix(sandbox-control): the orphan sweep no longer stops warm-pool VMs (their record is the pool row)

This commit is contained in:
Neil
2026-11-09 19:49:24 -08:00
parent d5e57508cf
commit 78fce9977d
2 changed files with 33 additions and 0 deletions
@@ -240,6 +240,10 @@ async function clearOrphan(db: D1Database, kind: string, id: string) {
await db.prepare('DELETE FROM provider_orphans WHERE kind = ? AND provider_id = ? AND deleted_at IS NULL').bind(kind, id).run()
}
async function isPooledVm(db: D1Database, providerName: string): Promise<boolean> {
return (await db.prepare('SELECT 1 AS hit FROM sandbox_pool WHERE provider_name = ?').bind(providerName).first<{ hit: number }>()) !== null
}
async function sweepOrphans(deps: LifecycleDeps, summary: HousekeepingSummary): Promise<void> {
const now = deps.now()
const grace = retention(deps).orphanGraceMs
@@ -251,6 +255,11 @@ async function sweepOrphans(deps: LifecycleDeps, summary: HousekeepingSummary):
if (box.tags.orcaEnv !== env || !box.tags.orcaSandbox) {
continue
}
// A warm-pool VM has no sandboxes row until it is claimed; its record is the pool row.
if (box.tags.orcaPool && (await isPooledVm(deps.db, box.name))) {
await clearOrphan(deps.db, 'sandbox', box.name)
continue
}
const row = await getSandboxByProviderName(deps.db, box.name)
if (row && row.status !== 'destroyed') {
await clearOrphan(deps.db, 'sandbox', box.name)
@@ -30,6 +30,30 @@ async function setup() {
}
describe('orphan sweep', () => {
it('never stops a warm-pool VM: its record is the pool row, not a sandboxes row', async () => {
const { h, deps, sweep } = await setup()
const name = 'orca-test-sbx_pooled'
await h.vercel.createSandbox({
name,
snapshotId: GOLDEN,
vcpus: 4,
region: 'iad1',
timeoutMs: 30 * 60_000,
ports: [],
networkPolicy: { allow: {} },
tags: { orcaSandbox: 'sbx_pooled', orcaEnv: 'test', orcaPool: '1' },
snapshotExpirationMs: 0
})
await deps.db
.prepare(
`INSERT INTO sandbox_pool (id, provider_name, machine_size, region, snapshot_id, status, created_at, updated_at) VALUES ('sbx_pooled', ?, 'standard', 'iad1', ?, 'ready', 1, 1)`
)
.bind(name, GOLDEN)
.run()
expect((await sweep()).orphanSandboxesSuspended).toBe(0)
expect(h.vercel.boxes.get(name)!.status).toBe('running')
})
it('suspends a provider sandbox with no live D1 row, deletes it after the grace period, leaves tracked ones alone', async () => {
const { h, sweep } = await setup()
const tracked = await h.call('POST', '/v1/sandboxes', create('ws-1'))