From 690d74bdfeaa9afac3399169274450e28c59b081 Mon Sep 17 00:00:00 2001 From: Colby McHenry Date: Thu, 7 May 2026 21:28:40 -0500 Subject: [PATCH] perf(db): drop redundant idx_edges_source / idx_edges_target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both narrow indexes are fully covered by the existing (source, kind) and (target, kind) composites via SQLite's left-prefix scan, so they're dead weight on every write. Empirical measurements (from the spike script in PR #122 on a 50K-node / 250K-edge synthetic DB): - DB size: 34.7 MB → 27.0 MB (-22.2%) - Bulk insert (250K edges): 590ms → 431ms (1.37× faster) - source/target lookup latency: no regression Adds migration v4 to drop both on existing databases; fresh-DB schema no longer creates them. Co-Authored-By: Claude Opus 4.7 (1M context) --- __tests__/foundation.test.ts | 2 +- __tests__/pr19-improvements.test.ts | 2 +- src/db/migrations.ts | 13 ++++++++++++- src/db/schema.sql | 9 ++++++--- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/__tests__/foundation.test.ts b/__tests__/foundation.test.ts index 9ee437da..4e8f204a 100644 --- a/__tests__/foundation.test.ts +++ b/__tests__/foundation.test.ts @@ -305,7 +305,7 @@ describe('Database Connection', () => { const version = db.getSchemaVersion(); expect(version).not.toBeNull(); - expect(version?.version).toBe(3); + expect(version?.version).toBe(4); db.close(); }); diff --git a/__tests__/pr19-improvements.test.ts b/__tests__/pr19-improvements.test.ts index 5fbe17d7..d43dceb2 100644 --- a/__tests__/pr19-improvements.test.ts +++ b/__tests__/pr19-improvements.test.ts @@ -299,7 +299,7 @@ describe('Best-Candidate Resolution', () => { describe('Schema v2 Migration', () => { it.skipIf(!HAS_SQLITE)('should have correct current schema version', async () => { const { CURRENT_SCHEMA_VERSION } = await import('../src/db/migrations'); - expect(CURRENT_SCHEMA_VERSION).toBe(3); + expect(CURRENT_SCHEMA_VERSION).toBe(4); }); it.skipIf(!HAS_SQLITE)('should have migration for version 2', async () => { diff --git a/src/db/migrations.ts b/src/db/migrations.ts index 0a256dbc..1a8d1c54 100644 --- a/src/db/migrations.ts +++ b/src/db/migrations.ts @@ -9,7 +9,7 @@ import { SqliteDatabase } from './sqlite-adapter'; /** * Current schema version */ -export const CURRENT_SCHEMA_VERSION = 3; +export const CURRENT_SCHEMA_VERSION = 4; /** * Migration definition @@ -54,6 +54,17 @@ const migrations: Migration[] = [ `); }, }, + { + version: 4, + description: + 'Drop redundant idx_edges_source / idx_edges_target (covered by source_kind / target_kind composites)', + up: (db) => { + db.exec(` + DROP INDEX IF EXISTS idx_edges_source; + DROP INDEX IF EXISTS idx_edges_target; + `); + }, + }, ]; /** diff --git a/src/db/schema.sql b/src/db/schema.sql index dd0a9f06..b08c34f3 100644 --- a/src/db/schema.sql +++ b/src/db/schema.sql @@ -122,9 +122,12 @@ CREATE TRIGGER IF NOT EXISTS nodes_au AFTER UPDATE ON nodes BEGIN VALUES (NEW.rowid, NEW.id, NEW.name, NEW.qualified_name, NEW.docstring, NEW.signature); END; --- Edge indexes -CREATE INDEX IF NOT EXISTS idx_edges_source ON edges(source); -CREATE INDEX IF NOT EXISTS idx_edges_target ON edges(target); +-- Edge indexes. +-- idx_edges_source / idx_edges_target are intentionally omitted — +-- the (source, kind) and (target, kind) composites below cover the +-- corresponding source-only / target-only lookups via SQLite's +-- left-prefix scan, so the narrow indexes are dead weight on writes. +-- Migration v4 drops them on existing databases. CREATE INDEX IF NOT EXISTS idx_edges_kind ON edges(kind); CREATE INDEX IF NOT EXISTS idx_edges_source_kind ON edges(source, kind); CREATE INDEX IF NOT EXISTS idx_edges_target_kind ON edges(target, kind);