mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-04 20:21:35 +00:00
perf(sqlite): skip no-op automatic schema repair (#115193)
This commit is contained in:
committed by
GitHub
parent
0f41b401fd
commit
2cbbb00fba
@@ -36,6 +36,7 @@ import {
|
||||
import {
|
||||
detectOpenClawStateDatabaseSchemaMigrations,
|
||||
repairOpenClawStateDatabaseSchema,
|
||||
repairOpenClawStateDatabaseSchemaIfNeeded,
|
||||
type OpenClawStateDatabaseSchemaMigration,
|
||||
} from "../state/openclaw-state-db.js";
|
||||
import { resolveOpenClawStateSqlitePath } from "../state/openclaw-state-db.paths.js";
|
||||
@@ -956,7 +957,7 @@ export async function autoMigrateLegacyPluginDoctorState(params: {
|
||||
});
|
||||
const stateDir = resolveStateDir(env, params.homedir ?? os.homedir);
|
||||
const oauthDir = resolveOAuthDir(env, stateDir);
|
||||
const stateSchema = repairOpenClawStateDatabaseSchema({
|
||||
const stateSchema = repairOpenClawStateDatabaseSchemaIfNeeded({
|
||||
env: { ...env, OPENCLAW_STATE_DIR: stateDir },
|
||||
});
|
||||
const changes = [...stateDirResult.changes, ...stateSchema.changes];
|
||||
@@ -1347,9 +1348,11 @@ export async function autoMigrateLegacyState(params: {
|
||||
});
|
||||
const stateDir = resolveStateDir(env, homedir);
|
||||
autoMigrateChecked.add(`${path.resolve(stateDir)}\0${migrationMode}`);
|
||||
const stateSchema = repairOpenClawStateDatabaseSchema({
|
||||
env: { ...env, OPENCLAW_STATE_DIR: stateDir },
|
||||
});
|
||||
const stateSchemaOptions = { env: { ...env, OPENCLAW_STATE_DIR: stateDir } };
|
||||
const stateSchema =
|
||||
params.doctorOnlyStateMigrations === true
|
||||
? repairOpenClawStateDatabaseSchema(stateSchemaOptions)
|
||||
: repairOpenClawStateDatabaseSchemaIfNeeded(stateSchemaOptions);
|
||||
if (stateSchema.warnings.length > 0) {
|
||||
return {
|
||||
migrated: stateDirResult.migrated || stateSchema.changes.length > 0,
|
||||
|
||||
@@ -512,6 +512,25 @@ describe("state migrations", () => {
|
||||
detectionCase = { ...detected, stateDir, env };
|
||||
});
|
||||
|
||||
it("keeps automatic migration read-only when the shared schema is current", async () => {
|
||||
const root = await createTempDir();
|
||||
const stateDir = path.join(root, ".openclaw");
|
||||
const env = createEnv(stateDir);
|
||||
const databasePath = openOpenClawStateDatabase({ env }).path;
|
||||
closeOpenClawStateDatabaseForTest();
|
||||
|
||||
const writer = new DatabaseSync(databasePath);
|
||||
writer.exec("PRAGMA journal_mode = WAL; BEGIN IMMEDIATE;");
|
||||
try {
|
||||
await expect(
|
||||
autoMigrateLegacyState({ cfg: createConfig(), env, homedir: () => root }),
|
||||
).resolves.toMatchObject({ changes: [], warnings: [] });
|
||||
} finally {
|
||||
writer.exec("ROLLBACK;");
|
||||
writer.close();
|
||||
}
|
||||
});
|
||||
|
||||
it("uses the requested environment for plugin migration refresh and writes", async () => {
|
||||
const root = await createTempDir();
|
||||
const stateDir = path.join(root, "custom-state");
|
||||
|
||||
@@ -30,6 +30,7 @@ import {
|
||||
openOpenClawStateDatabase,
|
||||
OPENCLAW_STATE_SCHEMA_VERSION,
|
||||
repairOpenClawStateDatabaseSchema,
|
||||
repairOpenClawStateDatabaseSchemaIfNeeded,
|
||||
runOpenClawStateWriteTransaction,
|
||||
withOpenClawStateStartupMigrationCheckpointDatabase,
|
||||
} from "./openclaw-state-db.js";
|
||||
@@ -1068,6 +1069,32 @@ describe("openclaw state database", () => {
|
||||
).toThrow();
|
||||
});
|
||||
|
||||
it("skips exclusive repair when the automatic schema gate is already current", () => {
|
||||
const stateDir = createTempStateDir();
|
||||
const options = { env: { OPENCLAW_STATE_DIR: stateDir } };
|
||||
const databasePath = openOpenClawStateDatabase(options).path;
|
||||
closeOpenClawStateDatabaseForTest();
|
||||
|
||||
const { DatabaseSync } = requireNodeSqlite();
|
||||
const before = new DatabaseSync(databasePath);
|
||||
before.prepare("UPDATE schema_meta SET updated_at = 123 WHERE meta_key = 'primary'").run();
|
||||
before.close();
|
||||
|
||||
expect(repairOpenClawStateDatabaseSchemaIfNeeded(options)).toEqual({
|
||||
changes: [],
|
||||
warnings: [],
|
||||
});
|
||||
|
||||
const after = new DatabaseSync(databasePath, { readOnly: true });
|
||||
try {
|
||||
expect(
|
||||
after.prepare("SELECT updated_at FROM schema_meta WHERE meta_key = 'primary'").get(),
|
||||
).toEqual({ updated_at: 123 });
|
||||
} finally {
|
||||
after.close();
|
||||
}
|
||||
});
|
||||
|
||||
it("drops unreleased transient verification history on open", () => {
|
||||
const stateDir = createTempStateDir();
|
||||
const options = { env: { OPENCLAW_STATE_DIR: stateDir } };
|
||||
|
||||
@@ -64,6 +64,7 @@ import { ensureAdditiveStateColumns } from "./openclaw-state-db-schema-additive.
|
||||
import { tableExists } from "./openclaw-state-db-schema-helpers.js";
|
||||
import {
|
||||
assertCanonicalStateSchemaShape,
|
||||
detectOpenClawStateDatabaseSchemaMigrationsFromDatabase,
|
||||
dropLegacyStateTables,
|
||||
markCurrentStateSchemaVersion,
|
||||
repairAgentDatabasesCompositePrimaryKey,
|
||||
@@ -292,6 +293,41 @@ export function repairOpenClawStateDatabaseSchema(options: OpenClawStateDatabase
|
||||
}
|
||||
}
|
||||
|
||||
/** Skip the exclusive doctor repair when automatic migration sees a canonical current schema. */
|
||||
export function repairOpenClawStateDatabaseSchemaIfNeeded(
|
||||
options: OpenClawStateDatabaseOptions = {},
|
||||
): {
|
||||
changes: string[];
|
||||
warnings: string[];
|
||||
} {
|
||||
const pathname = resolveDatabasePath(options);
|
||||
if (!existsSync(pathname)) {
|
||||
return { changes: [], warnings: [] };
|
||||
}
|
||||
|
||||
let needsRepair = true;
|
||||
let database: DatabaseSync | undefined;
|
||||
try {
|
||||
database = openNodeSqliteDatabase(pathname, { readOnly: true });
|
||||
assertSupportedSchemaVersion(database, pathname);
|
||||
needsRepair =
|
||||
readSqliteUserVersion(database) !== OPENCLAW_STATE_SCHEMA_VERSION ||
|
||||
detectOpenClawStateDatabaseSchemaMigrationsFromDatabase(database, pathname).length > 0;
|
||||
if (!needsRepair) {
|
||||
assertCurrentStateRuntimeSchema(database, pathname);
|
||||
}
|
||||
} catch {
|
||||
// Preserve the repair path's existing diagnostics for unreadable or noncanonical databases.
|
||||
needsRepair = true;
|
||||
} finally {
|
||||
if (database?.isOpen) {
|
||||
database.close();
|
||||
}
|
||||
}
|
||||
|
||||
return needsRepair ? repairOpenClawStateDatabaseSchema(options) : { changes: [], warnings: [] };
|
||||
}
|
||||
|
||||
function ensureSchema(db: DatabaseSync, pathname: string): void {
|
||||
const now = Date.now();
|
||||
const kysely = getNodeSqliteKysely<OpenClawStateMetadataDatabase>(db);
|
||||
|
||||
Reference in New Issue
Block a user