From 9185e53da177416b0f090f8796883a00a4ef3eb1 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 10 Aug 2026 01:18:48 -0700 Subject: [PATCH] refactor(state): simplify ownership WAL retry Keep the immutable-first ownership probe and WAL-aware corruption retry in one bounded connection loop. The selected location makes the retry single-shot while preserving handle cleanup and the no-mutation cold-family path. --- src/state/openclaw-state-ownership.ts | 56 +++++++++++---------------- 1 file changed, 23 insertions(+), 33 deletions(-) diff --git a/src/state/openclaw-state-ownership.ts b/src/state/openclaw-state-ownership.ts index 2085c4bdf547..0459a7560992 100644 --- a/src/state/openclaw-state-ownership.ts +++ b/src/state/openclaw-state-ownership.ts @@ -115,25 +115,6 @@ export function inspectOpenClawStateOwnershipFromDatabase( return parseExternalOwnership(row.value_json, databasePath); } -function hasLiveWal(databasePath: string): boolean { - return existsSync(`${databasePath}-wal`); -} - -function inspectOpenClawStateOwnershipAtLocation( - databasePath: string, - location = databasePath, -): OpenClawExternalStateOwnership | null { - const database = openNodeSqliteDatabase(location, { readOnly: true }); - try { - database.exec( - `PRAGMA busy_timeout = ${OPENCLAW_SQLITE_BUSY_TIMEOUT_MS}; PRAGMA query_only = ON; PRAGMA trusted_schema = OFF;`, - ); - return inspectOpenClawStateOwnershipFromDatabase(database, databasePath); - } finally { - database.close(); - } -} - /** Inspect one resolved state database path without joining the writable lifecycle. */ export function inspectOpenClawStateOwnershipAtPath( databasePath: string, @@ -142,21 +123,30 @@ export function inspectOpenClawStateOwnershipAtPath( if (!existsSync(resolvedPath)) { return null; } - if (hasLiveWal(resolvedPath)) { - return inspectOpenClawStateOwnershipAtLocation(resolvedPath); - } - try { - return inspectOpenClawStateOwnershipAtLocation( - resolvedPath, - resolveImmutableSqliteFileUri(resolvedPath), - ); - } catch (error) { - // External claims checkpoint before returning, so the main file is authoritative. - // If a WAL appeared during this open, retry with SQLite's normal WAL-aware reader. - if (!isSqliteCorruptionError(error) || !hasLiveWal(resolvedPath)) { - throw error; + let location = existsSync(`${resolvedPath}-wal`) + ? resolvedPath + : resolveImmutableSqliteFileUri(resolvedPath); + while (true) { + const database = openNodeSqliteDatabase(location, { readOnly: true }); + try { + database.exec( + `PRAGMA busy_timeout = ${OPENCLAW_SQLITE_BUSY_TIMEOUT_MS}; PRAGMA query_only = ON; PRAGMA trusted_schema = OFF;`, + ); + return inspectOpenClawStateOwnershipFromDatabase(database, resolvedPath); + } catch (error) { + // External claims checkpoint before returning, so the main file is authoritative. + // If a WAL appeared during immutable inspection, retry once with its live reader. + if ( + location === resolvedPath || + !isSqliteCorruptionError(error) || + !existsSync(`${resolvedPath}-wal`) + ) { + throw error; + } + location = resolvedPath; + } finally { + database.close(); } - return inspectOpenClawStateOwnershipAtLocation(resolvedPath); } }