From b2d95d8036945c6ae099f8641a3125b95b797097 Mon Sep 17 00:00:00 2001 From: Yevanchen Date: Thu, 30 Jul 2026 04:14:21 +0800 Subject: [PATCH] fix(runtime): prepare checkpoint directories before backup --- .../runtime/infrastructure/sandbox-backup-platform.ts | 8 +++++--- apps/api/tests/runtime-subject-recycle.test.ts | 7 ++++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/apps/api/src/modules/runtime/infrastructure/sandbox-backup-platform.ts b/apps/api/src/modules/runtime/infrastructure/sandbox-backup-platform.ts index 4ee9ae7c..6a93a34e 100644 --- a/apps/api/src/modules/runtime/infrastructure/sandbox-backup-platform.ts +++ b/apps/api/src/modules/runtime/infrastructure/sandbox-backup-platform.ts @@ -32,8 +32,9 @@ export async function createRuntimeSandboxBackup( return withDisposedRpcResource( await getRuntimeSubjectKeepAliveHandle(bindings, input.sandboxId), - async (sandbox) => - withDisposedRpcResult( + async (sandbox) => { + await sandbox.mkdir(input.dir, { recursive: true }); + return withDisposedRpcResult( sandbox.createBackup({ dir: input.dir, ttl: input.ttlSeconds, @@ -42,7 +43,8 @@ export async function createRuntimeSandboxBackup( dir: result.dir, id: encodeSandboxBackupIdForStorage(result.id), }), - ), + ); + }, ); } diff --git a/apps/api/tests/runtime-subject-recycle.test.ts b/apps/api/tests/runtime-subject-recycle.test.ts index 8c496406..50564d56 100644 --- a/apps/api/tests/runtime-subject-recycle.test.ts +++ b/apps/api/tests/runtime-subject-recycle.test.ts @@ -191,7 +191,7 @@ function createSandboxHandle(): SandboxHandle { destroy: async () => {}, exec: unavailable, getSession: unavailable, - mkdir: unavailable, + mkdir: async () => {}, mountBucket: unavailable, readFile: unavailable, restoreBackup: unavailable, @@ -269,6 +269,7 @@ describe("runtime subject recycle", () => { ('01J0000000000000000000000U', 1, 'TERMINATED'); `); const checkpointDirs: string[] = []; + const preparedDirs: string[] = []; const lifecycleCalls: string[] = []; let backupIndex = 0; currentSandbox = { @@ -282,6 +283,9 @@ describe("runtime subject recycle", () => { } return { dir: options.dir, id }; }, + mkdir: async (path) => { + preparedDirs.push(path); + }, destroy: async () => { lifecycleCalls.push("destroy"); }, @@ -319,6 +323,7 @@ describe("runtime subject recycle", () => { "/workspace/se/session-1", "/workspace/se/session-2", ]); + expect(preparedDirs.toSorted()).toEqual(checkpointDirs.toSorted()); expect(lifecycleCalls).toEqual(["keepAlive:false", "destroy"]); await expect(readRuntimeSubjectRecycleRow(database)).resolves.toMatchObject({ last_error: null,