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
58 changes: 26 additions & 32 deletions src/domain/graph/builder/pipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -491,20 +491,13 @@ async function runPostNativeAnalysis(
}
}

// Wire up WAL checkpoint callbacks for the analysis engine
// Flush JS WAL pages once so Rust can see them, then no-op callbacks.
// Previously each feature called wal_checkpoint(TRUNCATE) individually
// (~68ms each × 3-4 features). One FULL checkpoint suffices.
if (ctx.nativeDb && ctx.engineOpts) {
ctx.engineOpts.suspendJsDb = () => {
ctx.db.pragma('wal_checkpoint(TRUNCATE)');
};
ctx.engineOpts.resumeJsDb = () => {
try {
ctx.nativeDb?.exec('PRAGMA wal_checkpoint(TRUNCATE)');
} catch (e) {
debug(
`resumeJsDb: WAL checkpoint failed (nativeDb may already be closed): ${toErrorMessage(e)}`,
);
}
};
ctx.db.pragma('wal_checkpoint(FULL)');
Comment on lines +494 to +498
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 "PASSIVE" comment doesn't match FULL in code

The comment on line 496 says "One PASSIVE checkpoint suffices" but the pragma immediately below it uses wal_checkpoint(FULL). The same mismatch appears in runPipelineStages (~line 754–761): "do ONE upfront PASSIVE checkpoint" while the code calls wal_checkpoint(FULL). FULL is the correct choice here — unlike PASSIVE, it waits for existing readers to finish before checkpointing, giving a stronger visibility guarantee. The comments just need to be updated.

Suggested change
// Flush JS WAL pages once so Rust can see them, then no-op callbacks.
// Previously each feature called wal_checkpoint(TRUNCATE) individually
// (~68ms each × 3-4 features). One PASSIVE checkpoint suffices.
if (ctx.nativeDb && ctx.engineOpts) {
ctx.engineOpts.suspendJsDb = () => {
ctx.db.pragma('wal_checkpoint(TRUNCATE)');
};
ctx.engineOpts.resumeJsDb = () => {
try {
ctx.nativeDb?.exec('PRAGMA wal_checkpoint(TRUNCATE)');
} catch (e) {
debug(
`resumeJsDb: WAL checkpoint failed (nativeDb may already be closed): ${toErrorMessage(e)}`,
);
}
};
ctx.db.pragma('wal_checkpoint(FULL)');
// Flush JS WAL pages once so Rust can see them, then no-op callbacks.
// Previously each feature called wal_checkpoint(TRUNCATE) individually
// (~68ms each × 3-4 features). One FULL checkpoint suffices.
if (ctx.nativeDb && ctx.engineOpts) {
ctx.db.pragma('wal_checkpoint(FULL)');

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c6f28a8 — updated both comments (line 496 and line 754) from "PASSIVE" to "FULL" to match the actual wal_checkpoint(FULL) pragma calls.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c6f28a8 — updated both comments (line 496 and line 754) from "PASSIVE" to "FULL" to match the actual wal_checkpoint(FULL) pragma calls.

ctx.engineOpts.suspendJsDb = () => {};
ctx.engineOpts.resumeJsDb = () => {};
}

try {
Expand Down Expand Up @@ -532,7 +525,9 @@ async function runPostNativeAnalysis(
warn(`Analysis phases failed after native build: ${toErrorMessage(err)}`);
}

// Close nativeDb after analyses
// Close nativeDb after analyses — TRUNCATE checkpoint flushes all Rust
// WAL writes so JS and external readers can see them. Runs once after
// all analysis features complete (not per-feature).
if (ctx.nativeDb) {
try {
ctx.nativeDb.exec('PRAGMA wal_checkpoint(TRUNCATE)');
Expand Down Expand Up @@ -753,25 +748,20 @@ async function runPipelineStages(ctx: PipelineContext): Promise<void> {
await buildEdges(ctx);
await buildStructure(ctx);

// Reopen nativeDb for feature modules (ast, cfg, complexity, dataflow)
// which use suspendJsDb/resumeJsDb WAL checkpoint before native writes.
// Reopen nativeDb for feature modules (ast, cfg, complexity, dataflow).
// Skip for small incremental builds — same rationale as insertNodes above.
//
// Perf: do ONE upfront FULL checkpoint to flush JS WAL pages so Rust
// can see the latest rows, then make suspendJsDb/resumeJsDb no-ops.
// Previously each feature called wal_checkpoint(TRUNCATE) individually
// (~68ms each × 3-4 features = ~200-270ms overhead on incremental builds).
if (ctx.nativeAvailable && !smallIncremental) {
reopenNativeDb(ctx, 'analyses');
if (ctx.nativeDb && ctx.engineOpts) {
ctx.db.pragma('wal_checkpoint(FULL)');
ctx.engineOpts.nativeDb = ctx.nativeDb;
ctx.engineOpts.suspendJsDb = () => {
ctx.db.pragma('wal_checkpoint(TRUNCATE)');
};
ctx.engineOpts.resumeJsDb = () => {
try {
ctx.nativeDb?.exec('PRAGMA wal_checkpoint(TRUNCATE)');
} catch (e) {
debug(
`resumeJsDb: WAL checkpoint failed (nativeDb may already be closed): ${toErrorMessage(e)}`,
);
}
};
ctx.engineOpts.suspendJsDb = () => {};
ctx.engineOpts.resumeJsDb = () => {};
}
if (!ctx.nativeDb && ctx.engineOpts) {
ctx.engineOpts.nativeDb = undefined;
Expand All @@ -782,11 +772,15 @@ async function runPipelineStages(ctx: PipelineContext): Promise<void> {

await runAnalyses(ctx);

// Keep nativeDb open through finalize so persistBuildMetadata, advisory
// checks, and count queries use the native path. closeDbPair inside
// finalize handles both connections. Refresh the JS db so it has a
// valid page cache in case finalize falls back to JS paths (#751).
// Flush Rust WAL writes (AST, complexity, CFG, dataflow) so the JS
// connection and any post-build readers can see them. One TRUNCATE
// here replaces the N per-feature resumeJsDb checkpoints (#checkpoint-opt).
if (ctx.nativeDb) {
try {
ctx.nativeDb.exec('PRAGMA wal_checkpoint(TRUNCATE)');
} catch (e) {
debug(`post-analyses WAL checkpoint failed: ${toErrorMessage(e)}`);
}
refreshJsDb(ctx);
}

Expand Down
6 changes: 3 additions & 3 deletions src/features/cfg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -404,9 +404,9 @@ export async function buildCFGData(
blocks: cfg.blocks.map((b) => ({
index: b.index,
blockType: b.type,
startLine: b.startLine ?? null,
endLine: b.endLine ?? null,
label: b.label ?? null,
startLine: b.startLine ?? undefined,
endLine: b.endLine ?? undefined,
label: b.label ?? undefined,
})),
edges: (cfg.edges || []).map((e) => ({
sourceIndex: e.sourceIndex,
Expand Down
Loading