Skip to content

refactor: cache prepared statements in applyEdgeTechniquesAfterNativeInsert chunk loop#1891

Open
carlos-alm wants to merge 1 commit into
fix/issue-1767-structure-chunked-stmt-cachefrom
fix/issue-1768-build-edges-technique-stmt-cache
Open

refactor: cache prepared statements in applyEdgeTechniquesAfterNativeInsert chunk loop#1891
carlos-alm wants to merge 1 commit into
fix/issue-1767-structure-chunked-stmt-cachefrom
fix/issue-1768-build-edges-technique-stmt-cache

Conversation

@carlos-alm

Copy link
Copy Markdown
Contributor

Summary

  • applyEdgeTechniquesAfterNativeInsert re-prepare()d two SQL statements from scratch on every chunk iteration instead of caching by chunk size, in a hot transactional edge-finalization path.
  • Adopted the shared getOrCreatePerDbChunkStmt primitive from Consider adopting chunk-size statement-cache helper in features/structure.ts::batchUpdateRoles #1767, using persistent per-db caching (not per-call): this function has two call sites within buildEdges() that both pass the identical ctx.db and can both execute in one incremental rebuild (unlike batchUpdateRoles's mutually-exclusive full/incremental paths), and per-db keying is required for correctness anyway since prepared statements are bound to their originating connection (e.g. branch-compare.ts calls buildGraph() twice against different DB connections in one process).

Closes #1768

Test plan

Stacked on #1890 (base branch fix/issue-1767-structure-chunked-stmt-cache) — only the build-edges.ts diff is this issue's change.

…Insert chunk loop

The technique/confidence backfill loop in applyEdgeTechniquesAfterNativeInsert
re-prepared its two UPDATE statements on every chunk iteration instead of
caching by chunk size. Apply the shared getOrCreatePerDbChunkStmt primitive
(src/shared/chunked-stmt-cache.ts) with a persistent per-db WeakMap cache,
matching the batchInsertEdges/batchInsertNodes caches in builder/helpers.ts —
this function can run twice within a single buildEdges() call against the
same db (once from insertNativeBulkEdges, once from reconnectReverseDepEdges),
so a fresh per-call cache would still miss across that second invocation.

Pure performance refactor: SQL text and behavior are unchanged.

Fixes #1768

Impact: 1 functions changed, 4 affected
@greptile-apps

greptile-apps Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR caches the two db.prepare() calls inside the chunk loop of applyEdgeTechniquesAfterNativeInsert that were previously recompiling SQL on every iteration, replacing them with the shared getOrCreatePerDbChunkStmt primitive from #1767. Two module-level WeakMap<BetterSqlite3Database, Map<number, SqliteStatement>> caches are introduced to scope statements per-db connection and per-chunk-size.

  • Replaces per-iteration db.prepare(...) calls for the technique backfill and confidence floor UPDATE statements with lazily cached equivalents keyed by (db, chunkSize).
  • Uses WeakMap keying on the db object to ensure independent connections (e.g. the branch-compare.ts dual-db scenario) never share prepared statements, and GC naturally releases cached statements when a connection is closed.
  • The SQL text for both UPDATE statements is unchanged; only the compile-once caching layer is new.

Confidence Score: 5/5

Safe to merge — the change is a pure caching layer with no SQL or logic modifications.

Both UPDATE statements' SQL text and parameter binding are byte-identical to the original. The WeakMap keying on the db object correctly isolates cached statements per connection, handles the dual-db branch-compare scenario, and lets GC release statements when a connection closes. Chunk-size keying correctly accommodates the shorter final chunk. The two remaining db.prepare() calls inside the transaction (taggedRows, dynamicKindRows) are outside the chunk loop and intentionally left uncached.

No files require special attention.

Important Files Changed

Filename Overview
src/domain/graph/builder/stages/build-edges.ts Adds two module-level WeakMap caches and switches the chunk loop's pair of db.prepare() calls to getOrCreatePerDbChunkStmt; logic, SQL text, and parameter binding are all correct.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant BE as buildEdges()
    participant AETANI as applyEdgeTechniquesAfterNativeInsert()
    participant Cache as techniqueBackfillStmtCache / confidenceFloorStmtCache (WeakMap)
    participant DB as BetterSqlite3Database

    BE->>AETANI: call (insertNativeBulkEdges path)
    AETANI->>DB: db.transaction(tx)
    loop chunk iteration
        AETANI->>Cache: getOrCreatePerDbChunkStmt(db, chunkSize, buildSql)
        alt cache miss (first call for this chunkSize)
            Cache->>DB: db.prepare(SQL)
            DB-->>Cache: SqliteStatement
        end
        Cache-->>AETANI: cached SqliteStatement
        AETANI->>DB: techniqueStmt.run(...chunk)
        AETANI->>DB: confidenceStmt.run(floor, floor, ...chunk)
    end

    BE->>AETANI: call (reconnectReverseDepEdges path, same db)
    AETANI->>DB: db.transaction(tx)
    loop chunk iteration
        AETANI->>Cache: getOrCreatePerDbChunkStmt(db, chunkSize, buildSql)
        note over Cache: cache HIT — statement reused across calls
        Cache-->>AETANI: cached SqliteStatement
        AETANI->>DB: techniqueStmt.run(...chunk)
        AETANI->>DB: confidenceStmt.run(floor, floor, ...chunk)
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant BE as buildEdges()
    participant AETANI as applyEdgeTechniquesAfterNativeInsert()
    participant Cache as techniqueBackfillStmtCache / confidenceFloorStmtCache (WeakMap)
    participant DB as BetterSqlite3Database

    BE->>AETANI: call (insertNativeBulkEdges path)
    AETANI->>DB: db.transaction(tx)
    loop chunk iteration
        AETANI->>Cache: getOrCreatePerDbChunkStmt(db, chunkSize, buildSql)
        alt cache miss (first call for this chunkSize)
            Cache->>DB: db.prepare(SQL)
            DB-->>Cache: SqliteStatement
        end
        Cache-->>AETANI: cached SqliteStatement
        AETANI->>DB: techniqueStmt.run(...chunk)
        AETANI->>DB: confidenceStmt.run(floor, floor, ...chunk)
    end

    BE->>AETANI: call (reconnectReverseDepEdges path, same db)
    AETANI->>DB: db.transaction(tx)
    loop chunk iteration
        AETANI->>Cache: getOrCreatePerDbChunkStmt(db, chunkSize, buildSql)
        note over Cache: cache HIT — statement reused across calls
        Cache-->>AETANI: cached SqliteStatement
        AETANI->>DB: techniqueStmt.run(...chunk)
        AETANI->>DB: confidenceStmt.run(floor, floor, ...chunk)
    end
Loading

Reviews (1): Last reviewed commit: "refactor: cache prepared statements in a..." | Re-trigger Greptile

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Codegraph Impact Analysis

1 functions changed4 callers affected across 2 files

  • applyEdgeTechniquesAfterNativeInsert in src/domain/graph/builder/stages/build-edges.ts:1939 (4 transitive callers)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant