Skip to content

Commit f084804

Browse files
committed
Finalize sandbox stop asynchronously
1 parent 2de6145 commit f084804

2 files changed

Lines changed: 42 additions & 24 deletions

File tree

apps/api/src/routes/sandboxes.ts

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,45 @@ const collectArtifactsOnStop = (
368368
),
369369
)
370370

371+
const finalizeSandboxStop = (
372+
sandboxId: string,
373+
idBytes: Uint8Array,
374+
orgId: string,
375+
actorId: string,
376+
) =>
377+
Effect.gen(function* () {
378+
// Collect artifacts before fully stopping (best-effort)
379+
yield* collectArtifactsOnStop(sandboxId, idBytes, orgId)
380+
381+
// Tell the node daemon to stop the VM (best-effort)
382+
const nodeClient = yield* NodeClient
383+
yield* nodeClient.stopSandbox({ sandboxId: idBytes }).pipe(
384+
Effect.catchAll(() => Effect.void),
385+
)
386+
387+
// Transition to stopped
388+
yield* SandboxRepo.pipe(
389+
Effect.flatMap((repo) =>
390+
repo.updateStatus(idBytes, orgId, 'stopped', {
391+
endedAt: new Date(),
392+
}),
393+
),
394+
)
395+
396+
const auditLog = yield* AuditLog
397+
yield* auditLog.append({
398+
orgId,
399+
actorId,
400+
action: 'sandbox.stop',
401+
resourceType: 'sandbox',
402+
resourceId: sandboxId,
403+
})
404+
}).pipe(
405+
Effect.catchAllCause((cause) =>
406+
Effect.logError(`Sandbox stop finalization failed for ${sandboxId}: ${Cause.pretty(cause)}`),
407+
),
408+
)
409+
371410
const stopSandbox = Effect.gen(function* () {
372411
yield* requireScope('sandbox:write')
373412
const auth = yield* AuthContext
@@ -417,32 +456,11 @@ const stopSandbox = Effect.gen(function* () {
417456
failureReason: 'sandbox_stopped',
418457
})
419458

420-
// Collect artifacts before fully stopping (best-effort)
421-
yield* collectArtifactsOnStop(id, idBytes, auth.orgId)
422-
423-
// Tell the node daemon to stop the VM (best-effort)
424-
const nodeClient = yield* NodeClient
425-
yield* nodeClient.stopSandbox({ sandboxId: idBytes }).pipe(
426-
Effect.catchAll(() => Effect.void),
427-
)
428-
429-
// Transition to stopped
430-
const stopped = yield* repo.updateStatus(idBytes, auth.orgId, 'stopped', {
431-
endedAt: new Date(),
432-
})
433-
434-
const auditLog = yield* AuditLog
435-
yield* auditLog.append({
436-
orgId: auth.orgId,
437-
actorId: auth.userId,
438-
action: 'sandbox.stop',
439-
resourceType: 'sandbox',
440-
resourceId: id,
441-
})
459+
yield* Effect.forkDaemon(finalizeSandboxStop(id, idBytes, auth.orgId, auth.userId))
442460

443461
const response: StopSandboxResponse = {
444462
sandbox_id: id,
445-
status: stopped?.status ?? 'stopped',
463+
status: 'stopping',
446464
}
447465

448466
return HttpServerResponse.unsafeJson(response, { status: 202 })

packages/admin-cli/src/sandbox-smoke.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ export async function runSandboxSmokeTest(
477477
)
478478

479479
const expectedArtifactName = sharedPath.split('/').filter(Boolean).pop() ?? sharedPath
480-
const foundArtifact = await waitForArtifact(sandbox, expectedArtifactName, 5_000)
480+
const foundArtifact = await waitForArtifact(sandbox, expectedArtifactName, 15_000)
481481
assert(foundArtifact, `artifact list did not include ${expectedArtifactName}`)
482482
}),
483483
)

0 commit comments

Comments
 (0)