homegraph 1.4.1 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -11
- package/dist/bin/homegraph.js +295 -72
- package/dist/bin/homegraph.js.map +1 -1
- package/dist/context/index.d.ts.map +1 -1
- package/dist/context/index.js +32 -11
- package/dist/context/index.js.map +1 -1
- package/dist/db/index.d.ts +143 -1
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +293 -16
- package/dist/db/index.js.map +1 -1
- package/dist/db/queries.d.ts +43 -4
- package/dist/db/queries.d.ts.map +1 -1
- package/dist/db/queries.js +138 -14
- package/dist/db/queries.js.map +1 -1
- package/dist/db/sqlite-adapter.d.ts +24 -17
- package/dist/db/sqlite-adapter.d.ts.map +1 -1
- package/dist/db/sqlite-adapter.js +292 -36
- package/dist/db/sqlite-adapter.js.map +1 -1
- package/dist/db/wal-valve.d.ts +15 -2
- package/dist/db/wal-valve.d.ts.map +1 -1
- package/dist/db/wal-valve.js +88 -6
- package/dist/db/wal-valve.js.map +1 -1
- package/dist/extraction/arkts-batch-worker.js +3 -2
- package/dist/extraction/arkts-batch-worker.js.map +1 -1
- package/dist/extraction/default-ignore.d.ts +39 -0
- package/dist/extraction/default-ignore.d.ts.map +1 -0
- package/dist/extraction/default-ignore.js +221 -0
- package/dist/extraction/default-ignore.js.map +1 -0
- package/dist/extraction/index.d.ts +9 -10
- package/dist/extraction/index.d.ts.map +1 -1
- package/dist/extraction/index.js +227 -323
- package/dist/extraction/index.js.map +1 -1
- package/dist/extraction/languages/arkts.d.ts +90 -6
- package/dist/extraction/languages/arkts.d.ts.map +1 -1
- package/dist/extraction/languages/arkts.js +1579 -128
- package/dist/extraction/languages/arkts.js.map +1 -1
- package/dist/extraction/store-worker.d.ts +20 -0
- package/dist/extraction/store-worker.d.ts.map +1 -0
- package/dist/extraction/store-worker.js +102 -0
- package/dist/extraction/store-worker.js.map +1 -0
- package/dist/extraction/store-writer.d.ts +48 -0
- package/dist/extraction/store-writer.d.ts.map +1 -0
- package/dist/extraction/store-writer.js +167 -0
- package/dist/extraction/store-writer.js.map +1 -0
- package/dist/extraction/tree-sitter.d.ts.map +1 -1
- package/dist/extraction/tree-sitter.js +47 -0
- package/dist/extraction/tree-sitter.js.map +1 -1
- package/dist/extraction/wasm-runtime-flags.d.ts +14 -4
- package/dist/extraction/wasm-runtime-flags.d.ts.map +1 -1
- package/dist/extraction/wasm-runtime-flags.js +55 -7
- package/dist/extraction/wasm-runtime-flags.js.map +1 -1
- package/dist/index.d.ts +16 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +135 -97
- package/dist/index.js.map +1 -1
- package/dist/mcp/query-worker.d.ts +2 -2
- package/dist/mcp/query-worker.js +2 -2
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +42 -26
- package/dist/mcp/tools.js.map +1 -1
- package/dist/resolution/callback-synthesizer.d.ts +52 -16
- package/dist/resolution/callback-synthesizer.d.ts.map +1 -1
- package/dist/resolution/callback-synthesizer.js +234 -180
- package/dist/resolution/callback-synthesizer.js.map +1 -1
- package/dist/resolution/cooperative-yield.d.ts +10 -2
- package/dist/resolution/cooperative-yield.d.ts.map +1 -1
- package/dist/resolution/cooperative-yield.js +6 -4
- package/dist/resolution/cooperative-yield.js.map +1 -1
- package/dist/resolution/frameworks/go.d.ts.map +1 -1
- package/dist/resolution/frameworks/go.js +29 -6
- package/dist/resolution/frameworks/go.js.map +1 -1
- package/dist/resolution/import-resolver.d.ts +2 -3
- package/dist/resolution/import-resolver.d.ts.map +1 -1
- package/dist/resolution/import-resolver.js +166 -7
- package/dist/resolution/import-resolver.js.map +1 -1
- package/dist/resolution/index.d.ts +87 -2
- package/dist/resolution/index.d.ts.map +1 -1
- package/dist/resolution/index.js +739 -131
- package/dist/resolution/index.js.map +1 -1
- package/dist/resolution/memory-budget.d.ts +48 -0
- package/dist/resolution/memory-budget.d.ts.map +1 -0
- package/dist/resolution/memory-budget.js +162 -0
- package/dist/resolution/memory-budget.js.map +1 -0
- package/dist/resolution/name-matcher.d.ts +12 -1
- package/dist/resolution/name-matcher.d.ts.map +1 -1
- package/dist/resolution/name-matcher.js +439 -97
- package/dist/resolution/name-matcher.js.map +1 -1
- package/dist/resolution/resolver-pool.d.ts +106 -0
- package/dist/resolution/resolver-pool.d.ts.map +1 -0
- package/dist/resolution/resolver-pool.js +344 -0
- package/dist/resolution/resolver-pool.js.map +1 -0
- package/dist/resolution/resolver-worker.d.ts +17 -0
- package/dist/resolution/resolver-worker.d.ts.map +1 -0
- package/dist/resolution/resolver-worker.js +141 -0
- package/dist/resolution/resolver-worker.js.map +1 -0
- package/dist/resolution/types.d.ts +3 -0
- package/dist/resolution/types.d.ts.map +1 -1
- package/dist/spec/build/diff-parser.d.ts +55 -0
- package/dist/spec/build/diff-parser.d.ts.map +1 -0
- package/dist/spec/build/diff-parser.js +205 -0
- package/dist/spec/build/diff-parser.js.map +1 -0
- package/dist/spec/build/git-scanner.d.ts +93 -0
- package/dist/spec/build/git-scanner.d.ts.map +1 -0
- package/dist/spec/build/git-scanner.js +254 -0
- package/dist/spec/build/git-scanner.js.map +1 -0
- package/dist/spec/build/pipeline.d.ts +54 -0
- package/dist/spec/build/pipeline.d.ts.map +1 -0
- package/dist/spec/build/pipeline.js +170 -0
- package/dist/spec/build/pipeline.js.map +1 -0
- package/dist/spec/build/scan.d.ts +48 -0
- package/dist/spec/build/scan.d.ts.map +1 -0
- package/dist/spec/build/scan.js +89 -0
- package/dist/spec/build/scan.js.map +1 -0
- package/dist/spec/build/scope-resolver.d.ts +50 -0
- package/dist/spec/build/scope-resolver.d.ts.map +1 -0
- package/dist/spec/build/scope-resolver.js +105 -0
- package/dist/spec/build/scope-resolver.js.map +1 -0
- package/dist/spec/build/spec-extractor.d.ts +74 -0
- package/dist/spec/build/spec-extractor.d.ts.map +1 -0
- package/dist/spec/build/spec-extractor.js +344 -0
- package/dist/spec/build/spec-extractor.js.map +1 -0
- package/dist/spec/config.d.ts +35 -1
- package/dist/spec/config.d.ts.map +1 -1
- package/dist/spec/config.js +30 -5
- package/dist/spec/config.js.map +1 -1
- package/dist/spec/db/fts.d.ts.map +1 -1
- package/dist/spec/db/fts.js +19 -50
- package/dist/spec/db/fts.js.map +1 -1
- package/dist/spec/db/index.d.ts +5 -3
- package/dist/spec/db/index.d.ts.map +1 -1
- package/dist/spec/db/index.js +16 -1
- package/dist/spec/db/index.js.map +1 -1
- package/dist/spec/db/persist.d.ts +45 -0
- package/dist/spec/db/persist.d.ts.map +1 -0
- package/dist/spec/db/persist.js +66 -0
- package/dist/spec/db/persist.js.map +1 -0
- package/dist/spec/db/relations.d.ts +80 -1
- package/dist/spec/db/relations.d.ts.map +1 -1
- package/dist/spec/db/relations.js +175 -4
- package/dist/spec/db/relations.js.map +1 -1
- package/dist/spec/db/schema.d.ts +4 -0
- package/dist/spec/db/schema.d.ts.map +1 -1
- package/dist/spec/db/schema.js +4 -0
- package/dist/spec/db/schema.js.map +1 -1
- package/dist/spec/db/schema.sql +5 -1
- package/dist/spec/db/spec-node.d.ts +5 -0
- package/dist/spec/db/spec-node.d.ts.map +1 -1
- package/dist/spec/db/spec-node.js +11 -5
- package/dist/spec/db/spec-node.js.map +1 -1
- package/dist/spec/db/sql-utils.d.ts +11 -0
- package/dist/spec/db/sql-utils.d.ts.map +1 -0
- package/dist/spec/db/sql-utils.js +19 -0
- package/dist/spec/db/sql-utils.js.map +1 -0
- package/dist/spec/evolve/cluster-context.d.ts +30 -0
- package/dist/spec/evolve/cluster-context.d.ts.map +1 -0
- package/dist/spec/evolve/cluster-context.js +78 -0
- package/dist/spec/evolve/cluster-context.js.map +1 -0
- package/dist/spec/evolve/commit-spec-analyzer.d.ts +51 -0
- package/dist/spec/evolve/commit-spec-analyzer.d.ts.map +1 -0
- package/dist/spec/evolve/commit-spec-analyzer.js +88 -0
- package/dist/spec/evolve/commit-spec-analyzer.js.map +1 -0
- package/dist/spec/evolve/commit-spec-persister.d.ts +53 -0
- package/dist/spec/evolve/commit-spec-persister.d.ts.map +1 -0
- package/dist/spec/evolve/commit-spec-persister.js +93 -0
- package/dist/spec/evolve/commit-spec-persister.js.map +1 -0
- package/dist/spec/evolve/impact-locator.d.ts +23 -7
- package/dist/spec/evolve/impact-locator.d.ts.map +1 -1
- package/dist/spec/evolve/impact-locator.js +58 -14
- package/dist/spec/evolve/impact-locator.js.map +1 -1
- package/dist/spec/evolve/pipeline.d.ts +62 -25
- package/dist/spec/evolve/pipeline.d.ts.map +1 -1
- package/dist/spec/evolve/pipeline.js +436 -457
- package/dist/spec/evolve/pipeline.js.map +1 -1
- package/dist/spec/evolve/spec-rewriter.d.ts +13 -13
- package/dist/spec/evolve/spec-rewriter.d.ts.map +1 -1
- package/dist/spec/evolve/spec-rewriter.js +45 -45
- package/dist/spec/evolve/spec-rewriter.js.map +1 -1
- package/dist/spec/git/commits.d.ts +85 -0
- package/dist/spec/git/commits.d.ts.map +1 -0
- package/dist/spec/git/commits.js +218 -0
- package/dist/spec/git/commits.js.map +1 -0
- package/dist/spec/git/exec.d.ts +13 -0
- package/dist/spec/git/exec.d.ts.map +1 -0
- package/dist/spec/git/exec.js +19 -0
- package/dist/spec/git/exec.js.map +1 -0
- package/dist/spec/git/index.d.ts +9 -0
- package/dist/spec/git/index.d.ts.map +1 -0
- package/dist/spec/git/index.js +22 -0
- package/dist/spec/git/index.js.map +1 -0
- package/dist/spec/git-utils.d.ts +8 -0
- package/dist/spec/git-utils.d.ts.map +1 -0
- package/dist/spec/git-utils.js +14 -0
- package/dist/spec/git-utils.js.map +1 -0
- package/dist/spec/graph/index.d.ts +8 -0
- package/dist/spec/graph/index.d.ts.map +1 -0
- package/dist/spec/graph/index.js +17 -0
- package/dist/spec/graph/index.js.map +1 -0
- package/dist/spec/graph/queries.d.ts +6 -35
- package/dist/spec/graph/queries.d.ts.map +1 -1
- package/dist/spec/graph/queries.js +71 -248
- package/dist/spec/graph/queries.js.map +1 -1
- package/dist/spec/llm/agent-client.d.ts +59 -0
- package/dist/spec/llm/agent-client.d.ts.map +1 -0
- package/dist/spec/llm/agent-client.js +213 -0
- package/dist/spec/llm/agent-client.js.map +1 -0
- package/dist/spec/llm/agents/claude-code.d.ts +32 -0
- package/dist/spec/llm/agents/claude-code.d.ts.map +1 -0
- package/dist/spec/llm/agents/claude-code.js +125 -0
- package/dist/spec/llm/agents/claude-code.js.map +1 -0
- package/dist/spec/llm/agents/codex.d.ts +32 -0
- package/dist/spec/llm/agents/codex.d.ts.map +1 -0
- package/dist/spec/llm/agents/codex.js +145 -0
- package/dist/spec/llm/agents/codex.js.map +1 -0
- package/dist/spec/llm/agents/detect-utils.d.ts +28 -0
- package/dist/spec/llm/agents/detect-utils.d.ts.map +1 -0
- package/dist/spec/llm/agents/detect-utils.js +91 -0
- package/dist/spec/llm/agents/detect-utils.js.map +1 -0
- package/dist/spec/llm/agents/deveco-code.d.ts +33 -0
- package/dist/spec/llm/agents/deveco-code.d.ts.map +1 -0
- package/dist/spec/llm/agents/deveco-code.js +165 -0
- package/dist/spec/llm/agents/deveco-code.js.map +1 -0
- package/dist/spec/llm/agents/index.d.ts +31 -0
- package/dist/spec/llm/agents/index.d.ts.map +1 -0
- package/dist/spec/llm/agents/index.js +65 -0
- package/dist/spec/llm/agents/index.js.map +1 -0
- package/dist/spec/llm/agents/types.d.ts +76 -0
- package/dist/spec/llm/agents/types.d.ts.map +1 -0
- package/dist/spec/llm/agents/types.js +15 -0
- package/dist/spec/llm/agents/types.js.map +1 -0
- package/dist/spec/llm/client.d.ts +31 -11
- package/dist/spec/llm/client.d.ts.map +1 -1
- package/dist/spec/llm/client.js +143 -81
- package/dist/spec/llm/client.js.map +1 -1
- package/dist/spec/llm/factory.d.ts +41 -0
- package/dist/spec/llm/factory.d.ts.map +1 -0
- package/dist/spec/llm/factory.js +80 -0
- package/dist/spec/llm/factory.js.map +1 -0
- package/dist/spec/llm/prompts.d.ts +24 -8
- package/dist/spec/llm/prompts.d.ts.map +1 -1
- package/dist/spec/llm/prompts.js +100 -50
- package/dist/spec/llm/prompts.js.map +1 -1
- package/dist/spec/llm/retry.d.ts +53 -0
- package/dist/spec/llm/retry.d.ts.map +1 -0
- package/dist/spec/llm/retry.js +153 -0
- package/dist/spec/llm/retry.js.map +1 -0
- package/dist/spec/mine/clusterer.d.ts +63 -0
- package/dist/spec/mine/clusterer.d.ts.map +1 -0
- package/dist/spec/mine/clusterer.js +904 -0
- package/dist/spec/mine/clusterer.js.map +1 -0
- package/dist/spec/mine/clustering/features.d.ts +38 -0
- package/dist/spec/mine/clustering/features.d.ts.map +1 -0
- package/dist/spec/mine/clustering/features.js +108 -0
- package/dist/spec/mine/clustering/features.js.map +1 -0
- package/dist/spec/mine/clustering/index.d.ts +62 -0
- package/dist/spec/mine/clustering/index.d.ts.map +1 -0
- package/dist/spec/mine/clustering/index.js +259 -0
- package/dist/spec/mine/clustering/index.js.map +1 -0
- package/dist/spec/mine/clustering/leiden.d.ts +61 -0
- package/dist/spec/mine/clustering/leiden.d.ts.map +1 -0
- package/dist/spec/mine/clustering/leiden.js +489 -0
- package/dist/spec/mine/clustering/leiden.js.map +1 -0
- package/dist/spec/mine/clustering/text-similarity.d.ts +22 -0
- package/dist/spec/mine/clustering/text-similarity.d.ts.map +1 -0
- package/dist/spec/mine/clustering/text-similarity.js +98 -0
- package/dist/spec/mine/clustering/text-similarity.js.map +1 -0
- package/dist/spec/mine/generator.d.ts +54 -0
- package/dist/spec/mine/generator.d.ts.map +1 -0
- package/dist/spec/mine/generator.js +267 -0
- package/dist/spec/mine/generator.js.map +1 -0
- package/dist/spec/mine/persist.d.ts +37 -0
- package/dist/spec/mine/persist.d.ts.map +1 -0
- package/dist/spec/mine/persist.js +170 -0
- package/dist/spec/mine/persist.js.map +1 -0
- package/dist/spec/mine/pipeline.d.ts +41 -0
- package/dist/spec/mine/pipeline.d.ts.map +1 -0
- package/dist/spec/mine/pipeline.js +236 -0
- package/dist/spec/mine/pipeline.js.map +1 -0
- package/dist/spec/mine/progress-handler.d.ts +22 -0
- package/dist/spec/mine/progress-handler.d.ts.map +1 -0
- package/dist/spec/mine/progress-handler.js +108 -0
- package/dist/spec/mine/progress-handler.js.map +1 -0
- package/dist/spec/mine/progress.d.ts +23 -0
- package/dist/spec/mine/progress.d.ts.map +1 -0
- package/dist/spec/mine/progress.js +12 -0
- package/dist/spec/mine/progress.js.map +1 -0
- package/dist/spec/mine/scanner.d.ts +59 -0
- package/dist/spec/mine/scanner.d.ts.map +1 -0
- package/dist/spec/mine/scanner.js +383 -0
- package/dist/spec/mine/scanner.js.map +1 -0
- package/dist/spec/types.d.ts +32 -0
- package/dist/spec/types.d.ts.map +1 -1
- package/dist/spec/ui/index.d.ts +9 -0
- package/dist/spec/ui/index.d.ts.map +1 -0
- package/dist/spec/ui/index.js +16 -0
- package/dist/spec/ui/index.js.map +1 -0
- package/dist/spec/ui/progress-handler.d.ts +33 -0
- package/dist/spec/ui/progress-handler.d.ts.map +1 -0
- package/dist/spec/ui/progress-handler.js +132 -0
- package/dist/spec/ui/progress-handler.js.map +1 -0
- package/dist/spec/ui/progress.d.ts +24 -0
- package/dist/spec/ui/progress.d.ts.map +1 -0
- package/dist/spec/ui/progress.js +13 -0
- package/dist/spec/ui/progress.js.map +1 -0
- package/dist/spec/utils/fs.d.ts +45 -0
- package/dist/spec/utils/fs.d.ts.map +1 -0
- package/dist/spec/utils/fs.js +163 -0
- package/dist/spec/utils/fs.js.map +1 -0
- package/dist/spec/utils/index.d.ts +12 -0
- package/dist/spec/utils/index.d.ts.map +1 -0
- package/dist/spec/utils/index.js +26 -0
- package/dist/spec/utils/index.js.map +1 -0
- package/dist/spec/utils/meta.d.ts +32 -0
- package/dist/spec/utils/meta.d.ts.map +1 -0
- package/dist/spec/utils/meta.js +122 -0
- package/dist/spec/utils/meta.js.map +1 -0
- package/dist/spec/utils/truncate.d.ts +86 -0
- package/dist/spec/utils/truncate.d.ts.map +1 -0
- package/dist/spec/utils/truncate.js +172 -0
- package/dist/spec/utils/truncate.js.map +1 -0
- package/dist/spec/utils.d.ts +4 -16
- package/dist/spec/utils.d.ts.map +1 -1
- package/dist/spec/utils.js +6 -58
- package/dist/spec/utils.js.map +1 -1
- package/dist/sync/watcher.d.ts +25 -1
- package/dist/sync/watcher.d.ts.map +1 -1
- package/dist/sync/watcher.js +76 -3
- package/dist/sync/watcher.js.map +1 -1
- package/dist/telemetry/index.js +3 -3
- package/dist/telemetry/index.js.map +1 -1
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +7 -3
|
@@ -2,39 +2,152 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* SQLite Adapter
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Three-tier selection (first success wins):
|
|
6
|
+
* 1. `node:sqlite` (`DatabaseSync`) — real SQLite + WAL + FTS5, Node ≥22.5
|
|
7
|
+
* 2. `better-sqlite3` — native addon (optionalDependency)
|
|
8
|
+
* 3. `node-sqlite3-wasm` — last-resort fallback (no WAL; slower / lock-prone)
|
|
8
9
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* wasm fallback. When run from source instead, it requires Node >= 22.5.
|
|
10
|
+
* Library hosts on Node 20–22.4 land on (2) or (3). Node ≥22.5 prefers (1) and
|
|
11
|
+
* skips the native build. Override with `HOMEGRAPH_SQLITE_BACKEND=node-sqlite|native|wasm`.
|
|
12
12
|
*/
|
|
13
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.WASM_FALLBACK_FIX_RECIPE = void 0;
|
|
15
|
+
exports.buildWasmFallbackBanner = buildWasmFallbackBanner;
|
|
16
|
+
exports.isNodeSqliteAvailable = isNodeSqliteAvailable;
|
|
17
|
+
exports.isNativeSqliteAvailable = isNativeSqliteAvailable;
|
|
14
18
|
exports.createDatabase = createDatabase;
|
|
19
|
+
/**
|
|
20
|
+
* One-line recovery hint when WASM is active (no WAL — slower / lock-prone).
|
|
21
|
+
*/
|
|
22
|
+
exports.WASM_FALLBACK_FIX_RECIPE = 'upgrade to Node.js 22.5+ (uses built-in node:sqlite), or ' +
|
|
23
|
+
'`xcode-select --install` (macOS) / `apt install build-essential` (Debian/Ubuntu) ' +
|
|
24
|
+
'then `npm rebuild better-sqlite3`.';
|
|
25
|
+
/**
|
|
26
|
+
* Banner shown to stderr when falling back to WASM.
|
|
27
|
+
*/
|
|
28
|
+
function buildWasmFallbackBanner(priorErrors) {
|
|
29
|
+
const sep = '─'.repeat(72);
|
|
30
|
+
const lines = [
|
|
31
|
+
sep,
|
|
32
|
+
'[HomeGraph] WASM SQLite fallback active (node:sqlite + better-sqlite3 unavailable)',
|
|
33
|
+
sep,
|
|
34
|
+
'Indexing and sync will be 5-10x slower than a WAL backend, and concurrent',
|
|
35
|
+
'reads can hit "database is locked". Prefer Node 22.5+ or better-sqlite3.',
|
|
36
|
+
'',
|
|
37
|
+
'Fix — use built-in SQLite (best when you can):',
|
|
38
|
+
' Use Node.js 22.5 or newer, then restart homegraph',
|
|
39
|
+
'',
|
|
40
|
+
'Fix — native better-sqlite3:',
|
|
41
|
+
' macOS: xcode-select --install && npm rebuild better-sqlite3',
|
|
42
|
+
' Linux: sudo apt install build-essential python3 make && npm rebuild better-sqlite3',
|
|
43
|
+
' Any: npm install better-sqlite3 --save',
|
|
44
|
+
'',
|
|
45
|
+
'Verify after fix: `homegraph status` should show Backend: node-sqlite or native.',
|
|
46
|
+
];
|
|
47
|
+
if (priorErrors) {
|
|
48
|
+
lines.push('', `Prior load errors: ${priorErrors}`);
|
|
49
|
+
}
|
|
50
|
+
lines.push(sep);
|
|
51
|
+
return lines.join('\n');
|
|
52
|
+
}
|
|
53
|
+
function translateNamedParams(sql) {
|
|
54
|
+
const paramOrder = [];
|
|
55
|
+
const rewritten = sql.replace(/@(\w+)/g, (_match, name) => {
|
|
56
|
+
paramOrder.push(name);
|
|
57
|
+
return '?';
|
|
58
|
+
});
|
|
59
|
+
if (paramOrder.length === 0) {
|
|
60
|
+
return { sql, paramOrder: null };
|
|
61
|
+
}
|
|
62
|
+
return { sql: rewritten, paramOrder };
|
|
63
|
+
}
|
|
64
|
+
function resolveParams(params, paramOrder) {
|
|
65
|
+
if (params.length === 0)
|
|
66
|
+
return undefined;
|
|
67
|
+
if (paramOrder &&
|
|
68
|
+
params.length === 1 &&
|
|
69
|
+
params[0] !== null &&
|
|
70
|
+
typeof params[0] === 'object' &&
|
|
71
|
+
!Array.isArray(params[0]) &&
|
|
72
|
+
!(params[0] instanceof Buffer) &&
|
|
73
|
+
!(params[0] instanceof Uint8Array)) {
|
|
74
|
+
const obj = params[0];
|
|
75
|
+
return paramOrder.map(name => obj[name]);
|
|
76
|
+
}
|
|
77
|
+
if (params.length === 1)
|
|
78
|
+
return params[0];
|
|
79
|
+
return params;
|
|
80
|
+
}
|
|
81
|
+
function isBusyError(error) {
|
|
82
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
83
|
+
return /SQLITE_BUSY|database is locked/i.test(msg);
|
|
84
|
+
}
|
|
85
|
+
function sleepSync(ms) {
|
|
86
|
+
try {
|
|
87
|
+
const sab = new SharedArrayBuffer(4);
|
|
88
|
+
const ia = new Int32Array(sab);
|
|
89
|
+
Atomics.wait(ia, 0, 0, ms);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
const end = Date.now() + ms;
|
|
93
|
+
while (Date.now() < end) {
|
|
94
|
+
/* busy-wait */
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function withBusyRetry(fn, attempts = 10, baseDelayMs = 50) {
|
|
99
|
+
let last;
|
|
100
|
+
for (let i = 0; i < attempts; i++) {
|
|
101
|
+
try {
|
|
102
|
+
return fn();
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
last = error;
|
|
106
|
+
if (!isBusyError(error) || i === attempts - 1)
|
|
107
|
+
throw error;
|
|
108
|
+
sleepSync(baseDelayMs * (i + 1));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
throw last;
|
|
112
|
+
}
|
|
113
|
+
/** True when `require('node:sqlite').DatabaseSync` succeeds. */
|
|
114
|
+
function isNodeSqliteAvailable() {
|
|
115
|
+
try {
|
|
116
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
117
|
+
const mod = require('node:sqlite');
|
|
118
|
+
return typeof mod?.DatabaseSync === 'function';
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/** True when `require('better-sqlite3')` succeeds. */
|
|
125
|
+
function isNativeSqliteAvailable() {
|
|
126
|
+
try {
|
|
127
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
128
|
+
require('better-sqlite3');
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
15
135
|
/**
|
|
16
136
|
* Wraps Node's built-in `node:sqlite` (`DatabaseSync`) to match the
|
|
17
137
|
* better-sqlite3 interface the rest of the code expects.
|
|
18
|
-
*
|
|
19
|
-
* node:sqlite is real SQLite compiled into Node, so it supports WAL, FTS5,
|
|
20
|
-
* mmap, and `@named` params natively — the only shims needed are the
|
|
21
|
-
* better-sqlite3 conveniences node:sqlite omits: a `.pragma()` helper, a
|
|
22
|
-
* `.transaction()` helper, and `open` (node:sqlite exposes `isOpen`).
|
|
23
138
|
*/
|
|
24
139
|
class NodeSqliteAdapter {
|
|
25
140
|
_db;
|
|
26
|
-
|
|
141
|
+
_txDepth = 0;
|
|
142
|
+
constructor(dbPath, opts) {
|
|
27
143
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
28
144
|
const { DatabaseSync } = require('node:sqlite');
|
|
29
|
-
this._db = new DatabaseSync(dbPath);
|
|
145
|
+
this._db = opts?.readOnly ? new DatabaseSync(dbPath, { readOnly: true }) : new DatabaseSync(dbPath);
|
|
30
146
|
}
|
|
31
147
|
get open() {
|
|
32
148
|
return this._db.isOpen;
|
|
33
149
|
}
|
|
34
150
|
prepare(sql) {
|
|
35
|
-
// node:sqlite matches better-sqlite3's calling convention (variadic
|
|
36
|
-
// positional args, or a single object for @named params), so params forward
|
|
37
|
-
// through unchanged.
|
|
38
151
|
const stmt = this._db.prepare(sql);
|
|
39
152
|
return {
|
|
40
153
|
run(...params) {
|
|
@@ -60,14 +173,10 @@ class NodeSqliteAdapter {
|
|
|
60
173
|
}
|
|
61
174
|
pragma(str, options) {
|
|
62
175
|
const trimmed = str.trim();
|
|
63
|
-
// Write pragma ("key = value"): node:sqlite is real SQLite, so every pragma
|
|
64
|
-
// (WAL, mmap, synchronous, …) applies as-is.
|
|
65
176
|
if (trimmed.includes('=')) {
|
|
66
177
|
this._db.exec(`PRAGMA ${trimmed}`);
|
|
67
178
|
return;
|
|
68
179
|
}
|
|
69
|
-
// Read pragma. Default: the row object (e.g. { journal_mode: 'wal' }).
|
|
70
|
-
// `{ simple: true }` returns just the single column value, like better-sqlite3.
|
|
71
180
|
const row = this._db.prepare(`PRAGMA ${trimmed}`).get();
|
|
72
181
|
if (options?.simple) {
|
|
73
182
|
return row && typeof row === 'object' ? Object.values(row)[0] : row;
|
|
@@ -76,42 +185,189 @@ class NodeSqliteAdapter {
|
|
|
76
185
|
}
|
|
77
186
|
transaction(fn) {
|
|
78
187
|
return (...args) => {
|
|
188
|
+
if (this._txDepth > 0) {
|
|
189
|
+
this._txDepth++;
|
|
190
|
+
try {
|
|
191
|
+
return fn(...args);
|
|
192
|
+
}
|
|
193
|
+
finally {
|
|
194
|
+
this._txDepth--;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
79
197
|
this._db.exec('BEGIN');
|
|
198
|
+
this._txDepth = 1;
|
|
80
199
|
try {
|
|
81
200
|
const result = fn(...args);
|
|
82
201
|
this._db.exec('COMMIT');
|
|
202
|
+
this._txDepth = 0;
|
|
83
203
|
return result;
|
|
84
204
|
}
|
|
85
205
|
catch (error) {
|
|
86
206
|
this._db.exec('ROLLBACK');
|
|
207
|
+
this._txDepth = 0;
|
|
87
208
|
throw error;
|
|
88
209
|
}
|
|
89
210
|
};
|
|
90
211
|
}
|
|
91
212
|
close() {
|
|
92
|
-
// node:sqlite's DatabaseSync.close() throws if already closed; make it
|
|
93
|
-
// idempotent to match better-sqlite3 (callers may close more than once).
|
|
94
213
|
if (this._db.isOpen)
|
|
95
214
|
this._db.close();
|
|
96
215
|
}
|
|
97
216
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
217
|
+
class WasmDatabaseAdapter {
|
|
218
|
+
_db;
|
|
219
|
+
_openStmts = new Set();
|
|
220
|
+
_txDepth = 0;
|
|
221
|
+
constructor(dbPath, opts) {
|
|
222
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
223
|
+
const { Database } = require('node-sqlite3-wasm');
|
|
224
|
+
this._db = opts?.readOnly ? new Database(dbPath, 'readonly') : new Database(dbPath);
|
|
225
|
+
}
|
|
226
|
+
get open() {
|
|
227
|
+
return this._db.isOpen;
|
|
228
|
+
}
|
|
229
|
+
prepare(sql) {
|
|
230
|
+
const { sql: rewrittenSql, paramOrder } = translateNamedParams(sql);
|
|
231
|
+
const stmt = this._db.prepare(rewrittenSql);
|
|
232
|
+
this._openStmts.add(stmt);
|
|
233
|
+
return {
|
|
234
|
+
run(...params) {
|
|
235
|
+
const resolved = resolveParams(params, paramOrder);
|
|
236
|
+
const result = resolved !== undefined ? stmt.run(resolved) : stmt.run();
|
|
237
|
+
return {
|
|
238
|
+
changes: result?.changes ?? 0,
|
|
239
|
+
lastInsertRowid: result?.lastInsertRowid ?? 0,
|
|
240
|
+
};
|
|
241
|
+
},
|
|
242
|
+
get(...params) {
|
|
243
|
+
const resolved = resolveParams(params, paramOrder);
|
|
244
|
+
return withBusyRetry(() => resolved !== undefined ? stmt.get(resolved) : stmt.get());
|
|
245
|
+
},
|
|
246
|
+
all(...params) {
|
|
247
|
+
const resolved = resolveParams(params, paramOrder);
|
|
248
|
+
return withBusyRetry(() => resolved !== undefined ? stmt.all(resolved) : stmt.all());
|
|
249
|
+
},
|
|
250
|
+
iterate(...params) {
|
|
251
|
+
const resolved = resolveParams(params, paramOrder);
|
|
252
|
+
if (typeof stmt.iterate === 'function') {
|
|
253
|
+
return withBusyRetry(() => resolved !== undefined ? stmt.iterate(resolved) : stmt.iterate());
|
|
254
|
+
}
|
|
255
|
+
const rows = withBusyRetry(() => resolved !== undefined ? stmt.all(resolved) : stmt.all());
|
|
256
|
+
return rows[Symbol.iterator]();
|
|
257
|
+
},
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
exec(sql) {
|
|
261
|
+
this._db.exec(sql);
|
|
262
|
+
}
|
|
263
|
+
pragma(str, options) {
|
|
264
|
+
const trimmed = str.trim();
|
|
265
|
+
if (trimmed.includes('=')) {
|
|
266
|
+
const eqIdx = trimmed.indexOf('=');
|
|
267
|
+
const key = trimmed.substring(0, eqIdx).trim();
|
|
268
|
+
const value = trimmed.substring(eqIdx + 1).trim();
|
|
269
|
+
if (key === 'journal_mode' && value.toUpperCase() === 'WAL') {
|
|
270
|
+
this._db.exec('PRAGMA journal_mode = DELETE');
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
if (key === 'mmap_size')
|
|
274
|
+
return;
|
|
275
|
+
if (key === 'synchronous' && value.toUpperCase() === 'NORMAL') {
|
|
276
|
+
this._db.exec('PRAGMA synchronous = FULL');
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
this._db.exec(`PRAGMA ${key} = ${value}`);
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
const readStmt = this._db.prepare(`PRAGMA ${trimmed}`);
|
|
283
|
+
const result = readStmt.get();
|
|
284
|
+
readStmt.finalize();
|
|
285
|
+
if (options?.simple) {
|
|
286
|
+
return result && typeof result === 'object' ? Object.values(result)[0] : result;
|
|
287
|
+
}
|
|
288
|
+
return result;
|
|
289
|
+
}
|
|
290
|
+
transaction(fn) {
|
|
291
|
+
return (...args) => {
|
|
292
|
+
if (this._txDepth > 0) {
|
|
293
|
+
this._txDepth++;
|
|
294
|
+
try {
|
|
295
|
+
return fn(...args);
|
|
296
|
+
}
|
|
297
|
+
finally {
|
|
298
|
+
this._txDepth--;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
this._db.exec('BEGIN');
|
|
302
|
+
this._txDepth = 1;
|
|
303
|
+
try {
|
|
304
|
+
const result = fn(...args);
|
|
305
|
+
this._db.exec('COMMIT');
|
|
306
|
+
this._txDepth = 0;
|
|
307
|
+
return result;
|
|
308
|
+
}
|
|
309
|
+
catch (error) {
|
|
310
|
+
this._db.exec('ROLLBACK');
|
|
311
|
+
this._txDepth = 0;
|
|
312
|
+
throw error;
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
close() {
|
|
317
|
+
for (const stmt of this._openStmts) {
|
|
318
|
+
try {
|
|
319
|
+
stmt.finalize();
|
|
320
|
+
}
|
|
321
|
+
catch {
|
|
322
|
+
/* already finalized */
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
this._openStmts.clear();
|
|
326
|
+
this._db.close();
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
function resolveBackendOrder() {
|
|
330
|
+
const raw = process.env.HOMEGRAPH_SQLITE_BACKEND?.trim().toLowerCase();
|
|
331
|
+
if (raw === 'node-sqlite' || raw === 'native' || raw === 'wasm') {
|
|
332
|
+
return [raw];
|
|
333
|
+
}
|
|
334
|
+
return ['node-sqlite', 'native', 'wasm'];
|
|
335
|
+
}
|
|
336
|
+
function tryOpenBackend(backend, dbPath, opts) {
|
|
106
337
|
try {
|
|
107
|
-
|
|
338
|
+
if (backend === 'node-sqlite') {
|
|
339
|
+
return { db: new NodeSqliteAdapter(dbPath, opts), backend: 'node-sqlite' };
|
|
340
|
+
}
|
|
341
|
+
if (backend === 'native') {
|
|
342
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
343
|
+
const Database = require('better-sqlite3');
|
|
344
|
+
const db = opts?.readOnly ? new Database(dbPath, { readonly: true }) : new Database(dbPath);
|
|
345
|
+
return { db: db, backend: 'native' };
|
|
346
|
+
}
|
|
347
|
+
return { db: new WasmDatabaseAdapter(dbPath, opts), backend: 'wasm' };
|
|
108
348
|
}
|
|
109
349
|
catch (error) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
350
|
+
return { error: error instanceof Error ? error.message : String(error) };
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Create a database connection.
|
|
355
|
+
* Order: node:sqlite → better-sqlite3 → node-sqlite3-wasm (unless overridden).
|
|
356
|
+
*/
|
|
357
|
+
function createDatabase(dbPath, opts) {
|
|
358
|
+
const order = resolveBackendOrder();
|
|
359
|
+
const errors = [];
|
|
360
|
+
for (const backend of order) {
|
|
361
|
+
const result = tryOpenBackend(backend, dbPath, opts);
|
|
362
|
+
if ('db' in result) {
|
|
363
|
+
if (result.backend === 'wasm') {
|
|
364
|
+
console.warn(buildWasmFallbackBanner(errors.join(' | ') || undefined));
|
|
365
|
+
}
|
|
366
|
+
return result;
|
|
367
|
+
}
|
|
368
|
+
errors.push(`${backend}: ${result.error}`);
|
|
115
369
|
}
|
|
370
|
+
throw new Error(`Failed to load any SQLite backend (tried ${order.join(' → ')}).\n` +
|
|
371
|
+
errors.map(e => ` ${e}`).join('\n'));
|
|
116
372
|
}
|
|
117
373
|
//# sourceMappingURL=sqlite-adapter.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite-adapter.js","sourceRoot":"","sources":["../../src/db/sqlite-adapter.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;AA8HH,wCAYC;AA5GD;;;;;;;;GAQG;AACH,MAAM,iBAAiB;IACb,GAAG,CAAM;IAEjB,YAAY,MAAc;QACxB,iEAAiE;QACjE,MAAM,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;QAChD,IAAI,CAAC,GAAG,GAAG,IAAI,YAAY,CAAC,MAAM,CAAC,CAAC;IACtC,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC;IACzB,CAAC;IAED,OAAO,CAAC,GAAW;QACjB,oEAAoE;QACpE,4EAA4E;QAC5E,qBAAqB;QACrB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACnC,OAAO;YACL,GAAG,CAAC,GAAG,MAAa;gBAClB,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC;gBAC9B,OAAO;oBACL,OAAO,EAAE,MAAM,CAAC,CAAC,EAAE,OAAO,IAAI,CAAC,CAAC;oBAChC,eAAe,EAAE,CAAC,EAAE,eAAe,IAAI,CAAC;iBACzC,CAAC;YACJ,CAAC;YACD,GAAG,CAAC,GAAG,MAAa;gBAClB,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC;YAC7B,CAAC;YACD,GAAG,CAAC,GAAG,MAAa;gBAClB,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC;YAC7B,CAAC;YACD,OAAO,CAAC,GAAG,MAAa;gBACtB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,MAAM,CAAC,CAAC;YACjC,CAAC;SACF,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,GAAW;QACd,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACrB,CAAC;IAED,MAAM,CAAC,GAAW,EAAE,OAA8B;QAChD,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;QAC3B,4EAA4E;QAC5E,6CAA6C;QAC7C,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1B,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,OAAO,EAAE,CAAC,CAAC;YACnC,OAAO;QACT,CAAC;QACD,uEAAuE;QACvE,gFAAgF;QAChF,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,OAAO,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC;QACxD,IAAI,OAAO,EAAE,MAAM,EAAE,CAAC;YACpB,OAAO,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QACtE,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAED,WAAW,CAAI,EAAyB;QACtC,OAAO,CAAC,GAAG,IAAW,EAAE,EAAE;YACxB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACvB,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;gBAC3B,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBACxB,OAAO,MAAM,CAAC;YAChB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;gBAC1B,MAAM,KAAK,CAAC;YACd,CAAC;QACH,CAAC,CAAC;IACJ,CAAC;IAED,KAAK;QACH,uEAAuE;QACvE,yEAAyE;QACzE,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM;YAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;IACxC,CAAC;CACF;AAED;;;;;;GAMG;AACH,SAAgB,cAAc,CAAC,MAAc;IAC3C,IAAI,CAAC;QACH,OAAO,EAAE,EAAE,EAAE,IAAI,iBAAiB,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC;IACvE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACnE,MAAM,IAAI,KAAK,CACb,8DAA8D;YAC9D,8EAA8E;YAC9E,2EAA2E;YAC3E,qBAAqB,GAAG,EAAE,CAC3B,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
1
|
+
{"version":3,"file":"sqlite-adapter.js","sourceRoot":"","sources":["../../src/db/sqlite-adapter.ts"],"names":[],"mappings":"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"}
|
package/dist/db/wal-valve.d.ts
CHANGED
|
@@ -48,11 +48,10 @@ import type { DatabaseConnection } from './index';
|
|
|
48
48
|
* Resolve the valve's soft threshold from the `HOMEGRAPH_WAL_VALVE_MB`
|
|
49
49
|
* override; non-numeric / non-positive values fall back to the default.
|
|
50
50
|
*/
|
|
51
|
-
export declare function resolveWalValveMb(envVal: string | undefined): number;
|
|
51
|
+
export declare function resolveWalValveMb(envVal: string | undefined, dbSizeBytes?: number): number;
|
|
52
52
|
export declare class WalCheckpointValve {
|
|
53
53
|
private readonly db;
|
|
54
54
|
private readonly intervalMs;
|
|
55
|
-
private readonly log;
|
|
56
55
|
private timer;
|
|
57
56
|
private inflight;
|
|
58
57
|
/** Writer pause in progress (hard cap breached): passes loop until a full backfill. */
|
|
@@ -65,7 +64,21 @@ export declare class WalCheckpointValve {
|
|
|
65
64
|
private sizeAtLastFullBackfill;
|
|
66
65
|
private readonly softBytes;
|
|
67
66
|
private readonly hardBytes;
|
|
67
|
+
private readonly fileCapBytes;
|
|
68
|
+
/**
|
|
69
|
+
* Futility latch: consecutive backfill give-ups (a reader pinning the WAL)
|
|
70
|
+
* disable further writer pauses for a cooldown, so a pinned phase degrades
|
|
71
|
+
* to the pre-valve behavior (unbounded WAL, folded when the pinner exits)
|
|
72
|
+
* instead of burning a 20-pass checkpoint attempt — each pass a worker
|
|
73
|
+
* thread + fresh connection — at EVERY over-cap boundary. That churn is
|
|
74
|
+
* what turned a pinned kernel-scale resolution from slow into OOM-killed
|
|
75
|
+
* (§7a.1 run 1: 22GB WAL, exit 137 at an envelope the pre-fix build
|
|
76
|
+
* survived).
|
|
77
|
+
*/
|
|
78
|
+
private consecutiveGiveUps;
|
|
79
|
+
private futileUntil;
|
|
68
80
|
constructor(db: DatabaseConnection, softMb?: number, intervalMs?: number, log?: (msg: string) => void);
|
|
81
|
+
private readonly log;
|
|
69
82
|
private mb;
|
|
70
83
|
/** Un-backfilled growth estimate: bytes the WAL has grown past the last full backfill. */
|
|
71
84
|
private growthBytes;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wal-valve.d.ts","sourceRoot":"","sources":["../../src/db/wal-valve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"wal-valve.d.ts","sourceRoot":"","sources":["../../src/db/wal-valve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAalD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAc1F;AAED,qBAAa,kBAAkB;IA6B3B,OAAO,CAAC,QAAQ,CAAC,EAAE;IAEnB,OAAO,CAAC,QAAQ,CAAC,UAAU;IA9B7B,OAAO,CAAC,KAAK,CAA+C;IAC5D,OAAO,CAAC,QAAQ,CAA8B;IAC9C,uFAAuF;IACvF,OAAO,CAAC,KAAK,CAA8B;IAC3C;;;;OAIG;IACH,OAAO,CAAC,sBAAsB,CAAK;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IAEtC;;;;;;;;;OASG;IACH,OAAO,CAAC,kBAAkB,CAAK;IAC/B,OAAO,CAAC,WAAW,CAAK;gBAGL,EAAE,EAAE,kBAAkB,EACvC,MAAM,GAAE,MAA8D,EACrD,UAAU,GAAE,MAA0B,EACvD,GAAG,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAe;IAavC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAwB;IAE5C,OAAO,CAAC,EAAE;IAIV,0FAA0F;IAC1F,OAAO,CAAC,WAAW;IAInB,+EAA+E;IAC/E,KAAK,IAAI,IAAI;IAkBb,6EAA6E;IAC7E,IAAI,IAAI,IAAI;IAOZ,6FAA6F;IAC7F,KAAK,IAAI,IAAI;IAIb;;;;;OAKG;IACH,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;IAqBpC,uDAAuD;IACjD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAO5B;;;;;;;OAOG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ9B;;;;;;OAMG;YACW,aAAa;IAkC3B,OAAO,CAAC,IAAI;CA8Bb"}
|
package/dist/db/wal-valve.js
CHANGED
|
@@ -51,6 +51,8 @@ exports.resolveWalValveMb = resolveWalValveMb;
|
|
|
51
51
|
const DEFAULT_WAL_VALVE_MB = 256;
|
|
52
52
|
/** Hard cap = this × soft threshold; past it the writer pauses for a full backfill. */
|
|
53
53
|
const HARD_CAP_MULTIPLIER = 2;
|
|
54
|
+
/** File cap = this × soft threshold; past it the barrier also TRUNCATEs the file. */
|
|
55
|
+
const FILE_CAP_MULTIPLIER = 4;
|
|
54
56
|
/** Passes attempted per writer pause before giving up (a pinned reader could stall forever). */
|
|
55
57
|
const MAX_PAUSED_BACKFILL_PASSES = 20;
|
|
56
58
|
/** How often the timer looks at the WAL file size. */
|
|
@@ -59,18 +61,25 @@ const CHECK_INTERVAL_MS = 2000;
|
|
|
59
61
|
* Resolve the valve's soft threshold from the `HOMEGRAPH_WAL_VALVE_MB`
|
|
60
62
|
* override; non-numeric / non-positive values fall back to the default.
|
|
61
63
|
*/
|
|
62
|
-
function resolveWalValveMb(envVal) {
|
|
64
|
+
function resolveWalValveMb(envVal, dbSizeBytes) {
|
|
63
65
|
if (envVal !== undefined && envVal !== '') {
|
|
64
66
|
const n = Number(envVal);
|
|
65
67
|
if (Number.isFinite(n) && n > 0)
|
|
66
68
|
return Math.floor(n);
|
|
67
69
|
}
|
|
70
|
+
// Scale with the project when the caller knows the DB size: every fold
|
|
71
|
+
// re-writes hot B-tree pages into the main file (the #1231 pathology in
|
|
72
|
+
// bounded form — 111s of a kernel-scale batch loop at the flat 256MB cap,
|
|
73
|
+
// §7a.2), so a big project affords a proportionally bigger transient WAL
|
|
74
|
+
// (~dbSize/4 soft ⇒ file cap ≈ dbSize) in exchange for ~4× fewer folds.
|
|
75
|
+
if (dbSizeBytes !== undefined && dbSizeBytes > 0) {
|
|
76
|
+
return Math.min(2048, Math.max(DEFAULT_WAL_VALVE_MB, Math.floor(dbSizeBytes / 4 / (1024 * 1024))));
|
|
77
|
+
}
|
|
68
78
|
return DEFAULT_WAL_VALVE_MB;
|
|
69
79
|
}
|
|
70
80
|
class WalCheckpointValve {
|
|
71
81
|
db;
|
|
72
82
|
intervalMs;
|
|
73
|
-
log;
|
|
74
83
|
timer = null;
|
|
75
84
|
inflight = null;
|
|
76
85
|
/** Writer pause in progress (hard cap breached): passes loop until a full backfill. */
|
|
@@ -83,13 +92,33 @@ class WalCheckpointValve {
|
|
|
83
92
|
sizeAtLastFullBackfill = 0;
|
|
84
93
|
softBytes;
|
|
85
94
|
hardBytes;
|
|
95
|
+
fileCapBytes;
|
|
96
|
+
/**
|
|
97
|
+
* Futility latch: consecutive backfill give-ups (a reader pinning the WAL)
|
|
98
|
+
* disable further writer pauses for a cooldown, so a pinned phase degrades
|
|
99
|
+
* to the pre-valve behavior (unbounded WAL, folded when the pinner exits)
|
|
100
|
+
* instead of burning a 20-pass checkpoint attempt — each pass a worker
|
|
101
|
+
* thread + fresh connection — at EVERY over-cap boundary. That churn is
|
|
102
|
+
* what turned a pinned kernel-scale resolution from slow into OOM-killed
|
|
103
|
+
* (§7a.1 run 1: 22GB WAL, exit 137 at an envelope the pre-fix build
|
|
104
|
+
* survived).
|
|
105
|
+
*/
|
|
106
|
+
consecutiveGiveUps = 0;
|
|
107
|
+
futileUntil = 0;
|
|
86
108
|
constructor(db, softMb = resolveWalValveMb(process.env.HOMEGRAPH_WAL_VALVE_MB), intervalMs = CHECK_INTERVAL_MS, log = () => { }) {
|
|
87
109
|
this.db = db;
|
|
88
110
|
this.intervalMs = intervalMs;
|
|
89
|
-
this.log = log;
|
|
90
111
|
this.softBytes = softMb * 1024 * 1024;
|
|
91
112
|
this.hardBytes = this.softBytes * HARD_CAP_MULTIPLIER;
|
|
113
|
+
this.fileCapBytes = this.softBytes * FILE_CAP_MULTIPLIER;
|
|
114
|
+
// HOMEGRAPH_WAL_VALVE_DEBUG=1 surfaces valve decisions to stderr without
|
|
115
|
+
// needing the caller's verbose plumbing — the observability gap that let
|
|
116
|
+
// §7a.1 run 1 fail silently (give-ups were verbose-gated and invisible).
|
|
117
|
+
this.log = process.env.HOMEGRAPH_WAL_VALVE_DEBUG
|
|
118
|
+
? (m) => console.error(`[wal-valve] ${m}`)
|
|
119
|
+
: log;
|
|
92
120
|
}
|
|
121
|
+
log;
|
|
93
122
|
mb(n) {
|
|
94
123
|
return `${Math.round(n / 1024 / 1024)}MB`;
|
|
95
124
|
}
|
|
@@ -101,7 +130,19 @@ class WalCheckpointValve {
|
|
|
101
130
|
start() {
|
|
102
131
|
if (this.timer)
|
|
103
132
|
return;
|
|
104
|
-
|
|
133
|
+
// One armed line per run under either diagnostics env: §7a.1's failed
|
|
134
|
+
// kernel-scale runs burned three 25-minute cycles before "is the valve
|
|
135
|
+
// even alive?" could be answered.
|
|
136
|
+
if (process.env.HOMEGRAPH_SYNTH_TIMINGS || process.env.HOMEGRAPH_WAL_VALVE_DEBUG) {
|
|
137
|
+
console.error(`[wal-valve] armed soft=${this.mb(this.softBytes)} hard=${this.mb(this.hardBytes)} wal=${this.mb(this.db.getWalSizeBytes())}`);
|
|
138
|
+
}
|
|
139
|
+
let ticks = 0;
|
|
140
|
+
this.timer = setInterval(() => {
|
|
141
|
+
if ((++ticks % 15) === 0) {
|
|
142
|
+
this.log(`alive: wal=${this.mb(this.db.getWalSizeBytes())} baseline=${this.mb(this.sizeAtLastFullBackfill)} inflight=${this.inflight ? 'y' : 'n'} paused=${this.pause ? 'y' : 'n'}`);
|
|
143
|
+
}
|
|
144
|
+
this.check();
|
|
145
|
+
}, this.intervalMs);
|
|
105
146
|
this.timer.unref?.();
|
|
106
147
|
}
|
|
107
148
|
/** Stop watching. Any in-flight checkpoint keeps running — await drain(). */
|
|
@@ -125,7 +166,17 @@ class WalCheckpointValve {
|
|
|
125
166
|
backpressure() {
|
|
126
167
|
if (this.pause)
|
|
127
168
|
return this.pause;
|
|
128
|
-
if (
|
|
169
|
+
if (Date.now() < this.futileUntil)
|
|
170
|
+
return null; // pinned reader — parking is churn, not progress
|
|
171
|
+
// Two independent triggers:
|
|
172
|
+
// - growth: un-backfilled BACKLOG past the hard cap (the original valve).
|
|
173
|
+
// - file size: a WAL can stay fully backfilled and still grow without
|
|
174
|
+
// bound — the writer only restarts at frame 0 if a commit finds no
|
|
175
|
+
// reader marks, which §7a.1's instrumented run showed never happens
|
|
176
|
+
// in practice (file marched 361→721MB through two COMPLETE
|
|
177
|
+
// backfills). Past the file cap, park and TRUNCATE at the barrier —
|
|
178
|
+
// the backfill part is instant when the backlog is already folded.
|
|
179
|
+
if (this.growthBytes() <= this.hardBytes && this.db.getWalSizeBytes() <= this.fileCapBytes)
|
|
129
180
|
return null;
|
|
130
181
|
this.log(`backpressure: wal=${this.mb(this.db.getWalSizeBytes())} baseline=${this.mb(this.sizeAtLastFullBackfill)} — pausing writer for full backfill`);
|
|
131
182
|
const t0 = Date.now();
|
|
@@ -176,16 +227,39 @@ class WalCheckpointValve {
|
|
|
176
227
|
return; // checkpoint machinery unavailable — don't spin
|
|
177
228
|
this.log(`backfill pass ${i + 1}: busy=${res.busy} log=${res.log} checkpointed=${res.checkpointed} wal=${this.mb(this.db.getWalSizeBytes())}`);
|
|
178
229
|
if (res.busy === 0 && res.log === res.checkpointed) {
|
|
230
|
+
// Backfill complete AND we are at a parked barrier (backfillFully only
|
|
231
|
+
// runs under a writer pause): the no-reader window is guaranteed, so
|
|
232
|
+
// chop the FILE too — a fully-backfilled WAL otherwise keeps growing
|
|
233
|
+
// whenever commits land while pool readers hold marks (§7a.1: 22GB
|
|
234
|
+
// on-disk at kernel scale despite backfills). A racing reader turns
|
|
235
|
+
// this into a no-op (busy=1); the passive result above still stands.
|
|
236
|
+
const trunc = await this.db.checkpointWalTruncate();
|
|
237
|
+
if (trunc)
|
|
238
|
+
this.log(`truncate: busy=${trunc.busy} wal=${this.mb(this.db.getWalSizeBytes())}`);
|
|
179
239
|
this.sizeAtLastFullBackfill = this.db.getWalSizeBytes();
|
|
240
|
+
this.consecutiveGiveUps = 0;
|
|
241
|
+
this.futileUntil = 0;
|
|
180
242
|
return;
|
|
181
243
|
}
|
|
182
244
|
}
|
|
183
|
-
this.
|
|
245
|
+
this.consecutiveGiveUps++;
|
|
246
|
+
if (this.consecutiveGiveUps >= 2) {
|
|
247
|
+
this.futileUntil = Date.now() + 60_000;
|
|
248
|
+
}
|
|
249
|
+
const msg = `backfill gave up after ${MAX_PAUSED_BACKFILL_PASSES} passes (streak ${this.consecutiveGiveUps}${this.futileUntil ? ', parking disabled 60s' : ''}) — a reader is pinning the WAL`;
|
|
250
|
+
this.log(msg);
|
|
251
|
+
// Give-ups are rare and load-bearing for §7a.1-class diagnosis — surface
|
|
252
|
+
// them on any timing-instrumented run, not just valve-debug ones.
|
|
253
|
+
if (process.env.HOMEGRAPH_SYNTH_TIMINGS && !process.env.HOMEGRAPH_WAL_VALVE_DEBUG) {
|
|
254
|
+
console.error(`[wal-valve] ${msg}`);
|
|
255
|
+
}
|
|
184
256
|
}
|
|
185
257
|
fire() {
|
|
258
|
+
this.log(`fire: wal=${this.mb(this.db.getWalSizeBytes())} baseline=${this.mb(this.sizeAtLastFullBackfill)}`);
|
|
186
259
|
const p = this.db
|
|
187
260
|
.checkpointWalPassive()
|
|
188
261
|
.then((res) => {
|
|
262
|
+
this.log(`timer pass: ${res ? `busy=${res.busy} log=${res.log} checkpointed=${res.checkpointed}` : 'null (machinery unavailable)'} wal=${this.mb(this.db.getWalSizeBytes())}`);
|
|
189
263
|
// Full backfill (busy 0, every log frame checkpointed) ⇒ the writer's
|
|
190
264
|
// next commit wraps the WAL; the file's current size becomes the new
|
|
191
265
|
// growth baseline. A partial pass (writer appended during it, or a
|
|
@@ -194,6 +268,14 @@ class WalCheckpointValve {
|
|
|
194
268
|
// SQLite reports log = checkpointed = -1, which is harmless here.
|
|
195
269
|
if (res && res.busy === 0 && res.log === res.checkpointed) {
|
|
196
270
|
this.sizeAtLastFullBackfill = this.db.getWalSizeBytes();
|
|
271
|
+
// NO truncate here. A truncate checkpoint that starts against an
|
|
272
|
+
// ACTIVE writer wins the lock race and then blocks that writer for
|
|
273
|
+
// its entire backfill — after a multi-GB single-transaction burst
|
|
274
|
+
// (edge-index recreate) that exceeds the writer's 5s busy_timeout
|
|
275
|
+
// and fails the index with "database is locked" (§7a.2 record run).
|
|
276
|
+
// The file chop happens exclusively at parked barriers
|
|
277
|
+
// (backpressure/foldNow), where the writer is awaiting us by
|
|
278
|
+
// construction and cannot collide.
|
|
197
279
|
}
|
|
198
280
|
})
|
|
199
281
|
.catch(() => { })
|