fix: isolate uok message bus db per project
This commit is contained in:
parent
95cb13c08d
commit
2178aa8803
4 changed files with 72 additions and 4 deletions
|
|
@ -78,7 +78,7 @@ function openRawDb(path) {
|
|||
loadProvider();
|
||||
return new DatabaseSync(path);
|
||||
}
|
||||
const SCHEMA_VERSION = 38;
|
||||
const SCHEMA_VERSION = 39;
|
||||
function indexExists(db, name) {
|
||||
return !!db
|
||||
.prepare(
|
||||
|
|
@ -576,6 +576,16 @@ function initSchema(db, fileBacked) {
|
|||
tags TEXT NOT NULL DEFAULT '[]'
|
||||
)
|
||||
`);
|
||||
// content_hash is queried on every insert for deduplication; without an
|
||||
// index the lookup becomes a full table scan as ingestion volume grows.
|
||||
db.exec(
|
||||
"CREATE INDEX IF NOT EXISTS idx_memory_sources_content_hash ON memory_sources(content_hash)",
|
||||
);
|
||||
// Category GROUP BY queries (e.g. /sf memory stats) need a covering
|
||||
// index that filters active memories and groups by category.
|
||||
db.exec(
|
||||
"CREATE INDEX IF NOT EXISTS idx_memories_category ON memories(superseded_by, category)",
|
||||
);
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS milestones (
|
||||
id TEXT PRIMARY KEY,
|
||||
|
|
@ -2046,6 +2056,20 @@ function migrateSchema(db) {
|
|||
":applied_at": new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
if (currentVersion < 39) {
|
||||
db.exec(
|
||||
"CREATE INDEX IF NOT EXISTS idx_memory_sources_content_hash ON memory_sources(content_hash)",
|
||||
);
|
||||
db.exec(
|
||||
"CREATE INDEX IF NOT EXISTS idx_memories_category ON memories(superseded_by, category)",
|
||||
);
|
||||
db.prepare(
|
||||
"INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)",
|
||||
).run({
|
||||
":version": 39,
|
||||
":applied_at": new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
db.exec("COMMIT");
|
||||
} catch (err) {
|
||||
db.exec("ROLLBACK");
|
||||
|
|
|
|||
|
|
@ -201,7 +201,7 @@ test("openDatabase_migrates_v27_tasks_without_created_at_through_spec_backfill",
|
|||
const version = db
|
||||
.prepare("SELECT MAX(version) AS version FROM schema_version")
|
||||
.get();
|
||||
assert.equal(version.version, 38);
|
||||
assert.equal(version.version, 39);
|
||||
const taskSpec = db
|
||||
.prepare(
|
||||
"SELECT milestone_id, slice_id, task_id, verify FROM task_specs WHERE task_id = 'T01'",
|
||||
|
|
@ -285,3 +285,26 @@ test("openDatabase_memories_table_has_tags_column", () => {
|
|||
assert.equal(tagsCol.type, "TEXT");
|
||||
assert.equal(tagsCol.dflt_value, "'[]'");
|
||||
});
|
||||
|
||||
test("openDatabase_memory_indexes_exist", () => {
|
||||
assert.equal(openDatabase(":memory:"), true);
|
||||
const db = getDatabase();
|
||||
const indexes = db
|
||||
.prepare(
|
||||
"SELECT name FROM sqlite_master WHERE type = 'index' AND tbl_name IN ('memories', 'memory_sources')",
|
||||
)
|
||||
.all();
|
||||
const names = indexes.map((r) => r.name);
|
||||
assert.ok(
|
||||
names.includes("idx_memories_active"),
|
||||
"should have idx_memories_active",
|
||||
);
|
||||
assert.ok(
|
||||
names.includes("idx_memories_category"),
|
||||
"should have idx_memories_category",
|
||||
);
|
||||
assert.ok(
|
||||
names.includes("idx_memory_sources_content_hash"),
|
||||
"should have idx_memory_sources_content_hash",
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ import { afterEach, test } from "vitest";
|
|||
import {
|
||||
closeDatabase,
|
||||
getUokMessageBusMetrics,
|
||||
getUokMessagesForAgent,
|
||||
insertUokMessage,
|
||||
openDatabase,
|
||||
} from "../sf-db.js";
|
||||
import { AgentInbox, MessageBus } from "../uok/message-bus.js";
|
||||
|
||||
|
|
@ -274,3 +276,24 @@ test("getUokMessageBusMetrics_ignores_reads_by_non_recipient_for_unread_count",
|
|||
assert.equal(m.totalMessages, 1);
|
||||
assert.equal(m.totalUnread, 1);
|
||||
});
|
||||
|
||||
test("messageBus_send_when_switching_projects_uses_current_project_db", () => {
|
||||
const first = makeProject();
|
||||
const second = makeProject();
|
||||
|
||||
new MessageBus(first).send("agent-a", "agent-b", "first");
|
||||
new MessageBus(second).send("agent-a", "agent-b", "second");
|
||||
|
||||
closeDatabase();
|
||||
openDatabase(join(first, ".sf", "sf.db"));
|
||||
assert.deepEqual(
|
||||
getUokMessagesForAgent("agent-b").map((message) => message.body),
|
||||
["first"],
|
||||
);
|
||||
closeDatabase();
|
||||
openDatabase(join(second, ".sf", "sf.db"));
|
||||
assert.deepEqual(
|
||||
getUokMessagesForAgent("agent-b").map((message) => message.body),
|
||||
["second"],
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ import {
|
|||
getUokMessageReadIds,
|
||||
getUokMessagesForAgent,
|
||||
insertUokMessage,
|
||||
isDbAvailable,
|
||||
markUokMessageRead,
|
||||
openDatabase,
|
||||
} from "../sf-db.js";
|
||||
|
|
@ -34,7 +33,6 @@ function deterministicMessageId(key) {
|
|||
}
|
||||
|
||||
function ensureDb(basePath) {
|
||||
if (isDbAvailable()) return;
|
||||
const dir = sfRoot(basePath);
|
||||
mkdirSync(dir, { recursive: true });
|
||||
openDatabase(join(dir, "sf.db"));
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue