Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions packages/storage/src/__tests__/operational-state-store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,23 @@ test('shares one operational database and produces an online backup', async () =
}
});

test('new runtime databases use incremental vacuum before operational schema creation', async () => {
const root = await mkdtemp(join(tmpdir(), 'maka-operational-incremental-vacuum-'));
try {
const lease = acquireOperationalStateDatabase(root);
try {
assert.equal(
(lease.database.prepare('PRAGMA auto_vacuum').get() as { auto_vacuum: number }).auto_vacuum,
2,
);
} finally {
lease.close();
}
} finally {
await rm(root, { recursive: true, force: true });
}
});

test('atomically reapplies current owner schema without republishing its registry', async () => {
const root = await mkdtemp(join(tmpdir(), 'maka-operational-current-convergence-'));
const databasePath = join(root, 'runtime.sqlite');
Expand Down
42 changes: 42 additions & 0 deletions packages/storage/src/__tests__/sqlite-runtime-store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,48 @@ const PREFIX_PROOF_TEST_BUDGET = {
};

describe('SqliteRuntimeStore', () => {
it('leaves an existing NONE auto-vacuum database unchanged', async () => {
const root = await mkdtemp(join(tmpdir(), 'maka-sqlite-runtime-existing-none-'));
const dbPath = join(root, 'runtime.sqlite');
const existing = new DatabaseSync(dbPath);
existing.exec('CREATE TABLE existing_data (value TEXT NOT NULL)');
existing.close();
try {
const store = createSqliteRuntimeStore(dbPath);
store.close();
const inspected = new DatabaseSync(dbPath, { readOnly: true });
try {
assert.equal(
(inspected.prepare('PRAGMA auto_vacuum').get() as { auto_vacuum: number }).auto_vacuum,
0,
);
assert.ok(
inspected
.prepare("SELECT 1 FROM sqlite_schema WHERE type = 'table' AND name = 'existing_data'")
.get(),
);
} finally {
inspected.close();
}
} finally {
await rm(root, { recursive: true, force: true });
}
});

it('creates runtime databases with incremental auto-vacuum', async () => {
await withStore(async (_store, dbPath) => {
const inspected = new DatabaseSync(dbPath, { readOnly: true });
try {
assert.equal(
(inspected.prepare('PRAGMA auto_vacuum').get() as { auto_vacuum: number }).auto_vacuum,
2,
);
} finally {
inspected.close();
}
});
});

it('reads a complete Session from one snapshot while another connection commits its terminal', async (t) => {
await withStore(async (store, dbPath) => {
const opening = invocationOpeningEvent(1);
Expand Down
7 changes: 6 additions & 1 deletion packages/storage/src/operational-state-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { createRequire } from 'node:module';
import type { DatabaseSync } from 'node:sqlite';
import {
configureSqliteRuntimeDatabase,
configureSqliteRuntimeAutoVacuumForNewDatabase,
configureSqliteRuntimeLockWait,
migrateSqliteRuntimeDatabase,
readUserVersion,
Expand Down Expand Up @@ -208,7 +209,8 @@ class OperationalStateDatabaseOwner {
readonly databasePath: string,
options: OperationalStateDatabaseOptions,
) {
if (options.schemaMigration === 'require_current' && !existsSync(databasePath)) {
const databaseExisted = existsSync(databasePath);
if (options.schemaMigration === 'require_current' && !databaseExisted) {
throw new OperationalStateMigrationBlockedError(
new Error('Operational state has not been initialized by its Runtime Host'),
'requires_host_migration',
Expand All @@ -223,6 +225,9 @@ class OperationalStateDatabaseOwner {
try {
configureSqliteRuntimeLockWait(this.database);
this.database.exec('PRAGMA foreign_keys = ON');
if (options.schemaMigration !== 'require_current') {
configureSqliteRuntimeAutoVacuumForNewDatabase(this.database, databaseExisted);
}
if (options.schemaMigration === 'require_current') {
requireCurrentOperationalState(this.database);
} else {
Expand Down
19 changes: 19 additions & 0 deletions packages/storage/src/sqlite-runtime-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -954,6 +954,25 @@ export function configureSqliteRuntimeDatabase(db: DatabaseSync): void {
db.exec('PRAGMA foreign_keys = ON');
}

/** Set the reclamation mode before schema creation on a newly created file only. */
export function configureSqliteRuntimeAutoVacuumForNewDatabase(
db: DatabaseSync,
databaseExisted: boolean,
): void {
if (databaseExisted) return;
const existingSchema = db
.prepare("SELECT 1 AS present FROM sqlite_schema WHERE name NOT LIKE 'sqlite_%' LIMIT 1")
.get() as { present?: unknown } | undefined;
if (existingSchema?.present === 1) return;
db.exec('PRAGMA auto_vacuum = INCREMENTAL');
const configured = db.prepare('PRAGMA auto_vacuum').get() as
| { auto_vacuum?: unknown }
| undefined;
if (configured?.auto_vacuum !== 2) {
throw new Error('New SQLite runtime database did not enable incremental auto-vacuum');
}
}

/** Configure connection-local lock waiting without changing persistent database state. */
export function configureSqliteRuntimeLockWait(db: DatabaseSync): void {
db.exec(`PRAGMA busy_timeout = ${SQLITE_INITIALIZATION_BUSY_TIMEOUT_MS}`);
Expand Down
5 changes: 4 additions & 1 deletion packages/storage/src/sqlite-runtime-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export type {
ImmutableRuntimePrefixProofReadBudget,
} from './runtime-event-store-contract.js';

import { mkdirSync } from 'node:fs';
import { existsSync, mkdirSync } from 'node:fs';
import { createRequire } from 'node:module';
import { dirname } from 'node:path';
import type { DatabaseSync, SQLInputValue } from 'node:sqlite';
Expand Down Expand Up @@ -133,6 +133,7 @@ import {
} from '@maka/core/tool-recovery-bundle';
import {
configureSqliteRuntimeDatabase,
configureSqliteRuntimeAutoVacuumForNewDatabase,
migrateSqliteRuntimeDatabase,
readUserVersion,
RUNTIME_RECOVERY_AUTHORITY_CAPABILITY,
Expand Down Expand Up @@ -345,6 +346,7 @@ export class SqliteRuntimeStore
return;
}
const DatabaseSync = loadDatabaseSync();
const databaseExisted = path !== ':memory:' && existsSync(path);
this.db = options.readOnly
? new DatabaseSync(path, { readOnly: true })
: new DatabaseSync(path);
Expand All @@ -358,6 +360,7 @@ export class SqliteRuntimeStore
);
}
} else {
configureSqliteRuntimeAutoVacuumForNewDatabase(this.db, databaseExisted);
configureSqliteRuntimeDatabase(this.db);
migrateSqliteRuntimeDatabase(this.db);
}
Expand Down