mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-28 04:36:22 +00:00
fix: keep state database handles isolated
This commit is contained in:
@@ -110,6 +110,27 @@ describe("openclaw state database", () => {
|
||||
expect(fs.existsSync(databasePath)).toBe(true);
|
||||
});
|
||||
|
||||
it("keeps cached handles open when another state path is opened", () => {
|
||||
const firstPath = path.join(
|
||||
createTempStateDir(),
|
||||
"state",
|
||||
`first-${process.pid}-${Date.now()}.sqlite`,
|
||||
);
|
||||
const secondPath = path.join(
|
||||
createTempStateDir(),
|
||||
"state",
|
||||
`second-${process.pid}-${Date.now()}.sqlite`,
|
||||
);
|
||||
|
||||
const first = openOpenClawStateDatabase({ path: firstPath });
|
||||
const second = openOpenClawStateDatabase({ path: secondPath });
|
||||
|
||||
expect(first.db.isOpen).toBe(true);
|
||||
expect(second.db.isOpen).toBe(true);
|
||||
expect(openOpenClawStateDatabase({ path: firstPath })).toBe(first);
|
||||
expect(readSqliteNumberPragma(first.db, "user_version")).toBe(1);
|
||||
});
|
||||
|
||||
it("uses savepoints for nested write transaction rollback", () => {
|
||||
const stateDir = createTempStateDir();
|
||||
const options = { env: { OPENCLAW_STATE_DIR: stateDir } };
|
||||
|
||||
@@ -68,7 +68,7 @@ export type RecordOpenClawStateBackupRunOptions = OpenClawStateDatabaseOptions &
|
||||
manifest: Record<string, unknown>;
|
||||
};
|
||||
|
||||
let cachedDatabase: OpenClawStateDatabase | null = null;
|
||||
const cachedDatabases = new Map<string, OpenClawStateDatabase>();
|
||||
|
||||
type OpenClawStateMetadataDatabase = Pick<
|
||||
OpenClawStateKyselyDatabase,
|
||||
@@ -150,14 +150,14 @@ export function openOpenClawStateDatabase(
|
||||
): OpenClawStateDatabase {
|
||||
const env = options.env ?? process.env;
|
||||
const pathname = resolveDatabasePath(options);
|
||||
if (cachedDatabase && cachedDatabase.path === pathname) {
|
||||
return cachedDatabase;
|
||||
const cached = cachedDatabases.get(pathname);
|
||||
if (cached?.db.isOpen) {
|
||||
return cached;
|
||||
}
|
||||
if (cachedDatabase) {
|
||||
cachedDatabase.walMaintenance.close();
|
||||
clearNodeSqliteKyselyCacheForDatabase(cachedDatabase.db);
|
||||
cachedDatabase.db.close();
|
||||
cachedDatabase = null;
|
||||
if (cached) {
|
||||
cached.walMaintenance.close();
|
||||
clearNodeSqliteKyselyCacheForDatabase(cached.db);
|
||||
cachedDatabases.delete(pathname);
|
||||
}
|
||||
|
||||
ensureOpenClawStatePermissions(pathname, env);
|
||||
@@ -178,8 +178,9 @@ export function openOpenClawStateDatabase(
|
||||
throw err;
|
||||
}
|
||||
ensureOpenClawStatePermissions(pathname, env);
|
||||
cachedDatabase = { db, path: pathname, walMaintenance };
|
||||
return cachedDatabase;
|
||||
const database = { db, path: pathname, walMaintenance };
|
||||
cachedDatabases.set(pathname, database);
|
||||
return database;
|
||||
}
|
||||
|
||||
export function runOpenClawStateWriteTransaction<T>(
|
||||
@@ -273,17 +274,18 @@ export function recordOpenClawStateBackupRun(options: RecordOpenClawStateBackupR
|
||||
}
|
||||
|
||||
export function closeOpenClawStateDatabase(): void {
|
||||
if (!cachedDatabase) {
|
||||
return;
|
||||
for (const database of cachedDatabases.values()) {
|
||||
database.walMaintenance.close();
|
||||
clearNodeSqliteKyselyCacheForDatabase(database.db);
|
||||
if (database.db.isOpen) {
|
||||
database.db.close();
|
||||
}
|
||||
}
|
||||
cachedDatabase.walMaintenance.close();
|
||||
clearNodeSqliteKyselyCacheForDatabase(cachedDatabase.db);
|
||||
cachedDatabase.db.close();
|
||||
cachedDatabase = null;
|
||||
cachedDatabases.clear();
|
||||
}
|
||||
|
||||
export function isOpenClawStateDatabaseOpen(): boolean {
|
||||
return cachedDatabase?.db.isOpen === true;
|
||||
return Array.from(cachedDatabases.values()).some((database) => database.db.isOpen);
|
||||
}
|
||||
|
||||
export const closeOpenClawStateDatabaseForTest = closeOpenClawStateDatabase;
|
||||
|
||||
Reference in New Issue
Block a user