Skip to content

refactor: wrap all db usage in try/finally for reliable db.close()#384

Merged
carlos-alm merged 2 commits intomainfrom
refactor/try-finally-db-close
Mar 9, 2026
Merged

refactor: wrap all db usage in try/finally for reliable db.close()#384
carlos-alm merged 2 commits intomainfrom
refactor/try-finally-db-close

Conversation

@carlos-alm
Copy link
Contributor

Summary

35 functions refactored across 12 files:

File Functions db.close() calls removed
queries.js 16 (queryNameData, impactAnalysisData, moduleMapData, fileDepsData, fnDepsData, fnImpactData, pathData, diffImpactData, listFunctionsData, statsData, contextData, childrenData, explainData, whereData, rolesData, exportsData) ~30
embedder.js 3 (searchData, multiSearchData, ftsSearchData) 8
dataflow.js 3 (dataflowData, dataflowPathData, dataflowImpactData) 11
cfg.js 1 (cfgData) 3
flow.js 2 (listEntryPointsData, flowData) 3
structure.js 3 (structureData, hotspotsData, moduleBoundariesData) 3
owners.js 1 (ownersData) 2
triage.js 1 (triageData) 3
communities.js 1 (communitiesData) 1
ast.js 1 (astQueryData) 1
complexity.js 1 (complexityData) 2
branch-compare.js 2 (loadSymbolsFromDb, loadCallersFromDb) 3

Intentionally unchanged:

  • _prepareSearch in embedder.js — returns the open db handle to callers
  • hybridSearchData quick checkDb open/close — trivial inline pattern
  • Generator functions (iterListFunctions, iterRoles, iterWhere) — already use try/finally

Test plan

  • All 280 integration tests pass (queries, communities, triage, embedder, complexity, audit, owners, check, graph, sequence, flow)
  • Biome lint clean on all 12 files
  • Verified: every db.close() in modified files is inside a finally block (except the 3 intentional exceptions above)

Replace manual db.close() before each return with a single
try/finally block across all query functions. This ensures the
database connection is always released, even if an unexpected
error occurs mid-execution.

Extends the pattern from #383 (sequence.js) to the rest of the
codebase: queries.js (16 functions), embedder.js (3), dataflow.js
(3), cfg.js (1), flow.js (2), structure.js (3), owners.js (1),
triage.js (1), communities.js (1), ast.js (1), complexity.js (1),
branch-compare.js (2).

Impact: 42 functions changed, 163 affected
@claude
Copy link

claude bot commented Mar 9, 2026

Claude finished @carlos-alm's task —— View job


Reviewing PR: Database try/finally refactoring

Todo List:

  • Read CLAUDE.md for repo-specific context
  • Phase 1: Root cause analysis - understand the problem being solved
  • Phase 2: Configuration change detection - check for high-risk changes
  • Phase 3: Test integrity - verify no tests were weakened
  • Phase 4: Code quality review - examine the refactoring implementation
  • Provide final assessment with rating

Phase 1: Root Cause Analysis ✅

Problem Being Solved: SQLite connection resource leaks when functions exit early due to exceptions or early returns. The old pattern manually called db.close() before each return, but if an unexpected error occurred mid-execution, connections would remain open.

Example from queryNameData():

// OLD: Manual db.close() before each return (❌ resource leak risk)
if (nodes.length === 0) {
  db.close();  // Must remember to close before every return
  return { query: name, results: [] };
}
// ... more code that could throw ...
db.close();  // Final close, but unreachable if exception occurs

// NEW: Guaranteed cleanup with try/finally (✅ always closes)
try {
  if (nodes.length === 0) {
    return { query: name, results: [] };  // Early return OK
  }
  // ... code that might throw ...
} finally {
  db.close();  // Always executes, even on exceptions
}

Why This Approach: Standard resource management pattern. Ensures database connections are always released regardless of how the function exits (normal return, early return, or exception).

Risk Assessment: LOW RISK - This is a defensive programming improvement that reduces resource leaks without changing functionality.

Phase 2: Configuration Change Detection ✅

No high-risk configuration changes detected:

  • ✅ No linting/formatting rule changes
  • ✅ No test configuration modifications
  • ✅ No build configuration changes
  • ✅ Only source code files modified (12 .js files)
  • ✅ No TypeScript config, package.json, or workflow changes

Phase 3: Test Integrity ✅

No test weakening detected:

  • ✅ No test files modified (0 files in tests/)
  • ✅ No test expectations changed
  • ✅ PR claims "All 280 integration tests pass"
  • ✅ Single focused commit with clear intent
  • ✅ No changes to test timeouts, coverage thresholds, or assertions

Phase 4: Code Quality Review ✅

Excellent refactoring implementation:

Pattern Consistency: Every modified function follows identical try { ... } finally { db.close(); } pattern
Complete Coverage: All 35 functions across 12 files consistently refactored
Correct Exception Handling: Finally blocks guarantee resource cleanup even on exceptions
Intentional Exceptions Documented:

No Functional Changes: Logic preserved, only resource management improved
No Double-Close Risk: Each db handle opened once, closed once in finally block
Backward Compatibility: Public APIs unchanged

No issues found:

  • No missing try blocks
  • No leaked db handles
  • No double-close scenarios
  • No inconsistent patterns

Root Cause Analysis

Problem Being Solved: SQLite connection resource leaks when functions exit via exceptions or early returns
Why This Approach: Industry-standard resource management with guaranteed cleanup via finally blocks
Risk Assessment: LOW RISK - Defensive improvement with no functional changes

Critical Concerns

None identified. This is a textbook example of proper resource management refactoring.

Final Recommendation

  • Rating: ⭐⭐⭐⭐ (4/5)
  • Action: APPROVE
  • Reasoning: High-quality refactoring that eliminates resource leak potential. Consistent implementation across 35 functions, zero functional changes, proper exception handling, and no configuration weakening. This is exactly the type of defensive programming improvement that reduces technical debt and improves system reliability.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 9, 2026

Greptile Summary

This PR applies a consistent try/finally refactor across all 12 database query files to guarantee db.close() is called on every code path — including unexpected exceptions — eliminating a pre-existing class of connection leaks where an unhandled error would exit a function before reaching the manual db.close().

Key points:

  • 35 functions refactored across 12 files using a uniform try { ... } finally { db.close(); } pattern, replacing ~50+ per-return db.close() calls.
  • Behavior is preserved for all happy paths and early-exit guard clauses; the only net change is that exceptions now also guarantee cleanup.
  • communities.js uses a narrower try/finally scope — only the buildGraphologyGraph(db, ...) call is wrapped, with the Graphology graph processing left outside. This is correct and intentional since the post-build work doesn't access db, but the pattern is visually distinct from all other refactored files.
  • Intentional exclusions are clearly documented in the PR: _prepareSearch (returns db to caller), hybridSearchData's inline checkDb, and the generator functions already using try/finally.
  • The PR passes all 280 integration tests and linting is clean.

Confidence Score: 5/5

  • This PR is safe to merge — it is a pure resource-management refactor with no logic changes and full integration test coverage.
  • The change is mechanical and well-scoped: every function wraps its existing body in try/finally { db.close(); } with no modifications to query logic, return shapes, or error semantics. All early-exit paths are preserved inside the try block, nested try/catch blocks are handled correctly (the outer finally still fires on inner catch returns), and async functions in embedder.js work correctly with the pattern. The one structural variant in communities.js (narrower try scope) is intentional and correct. All 280 integration tests pass and linting is clean.
  • No files require special attention. src/communities.js uses a slightly narrower try/finally scope than the other files but this is intentional and correct.

Important Files Changed

Filename Overview
src/ast.js Single function wrapped in try/finally — minimal, correct change.
src/branch-compare.js Two functions (loadSymbolsFromDb, loadCallersFromDb) correctly wrapped in try/finally with all early returns moved inside the try block.
src/cfg.js Single cfgData function wrapped in try/finally; multiple early-exit guard paths now properly close the DB.
src/communities.js Only the DB-call portion (buildGraphologyGraph) is inside the try block; graph processing logic is correctly left outside since it doesn't use db.
src/complexity.js Complex nested try/catch/finally refactored correctly; inner catch blocks with early returns will still trigger the outer finally to close the DB.
src/dataflow.js Three functions (dataflowData, dataflowPathData, dataflowImpactData) correctly wrapped; multiple early returns moved inside try blocks.
src/embedder.js Three search functions (searchData, multiSearchData, ftsSearchData) wrapped correctly; _prepareSearch intentionally left unchanged as documented.
src/flow.js Both listEntryPointsData and flowData correctly wrapped; early return for missing entry node now properly closes DB.
src/owners.js ownersData correctly wrapped in try/finally; early return when CODEOWNERS is missing now properly closes DB.
src/queries.js 16 functions refactored consistently; the most complex file in the PR, all early returns and nested try/catch blocks are correctly wrapped.
src/structure.js Three structure query functions wrapped in try/finally, all early exits covered.
src/triage.js Single triageData function correctly wrapped in try/finally.

Sequence Diagram

sequenceDiagram
    participant Caller
    participant QueryFn as Query Function
    participant DB as SQLite DB

    Caller->>QueryFn: invoke
    QueryFn->>DB: openReadonlyOrFail()
    Note over QueryFn: try block begins
    alt Happy path
        QueryFn->>DB: db.prepare().all()
        DB-->>QueryFn: rows
        QueryFn->>DB: finally: db.close()
        QueryFn-->>Caller: return result
    else Early return guard
        QueryFn->>DB: finally: db.close()
        QueryFn-->>Caller: return empty result
    else Unexpected exception thrown
        QueryFn->>DB: finally: db.close()
        QueryFn-->>Caller: propagate error
    end
Loading

Last reviewed commit: c1d2695

@claude
Copy link

claude bot commented Mar 9, 2026

Claude finished @carlos-alm's task —— View job


Reviewing PR: Database try/finally refactoring

Todo List:

  • Read CLAUDE.md for repo-specific context
  • Phase 1: Root cause analysis - understand the problem being solved
  • Phase 2: Configuration change detection - check for high-risk changes
  • Phase 3: Test integrity - verify no tests were weakened
  • Phase 4: Code quality review - examine the refactoring implementation
  • Provide final assessment with rating

Phase 1: Root Cause Analysis ✅

Problem Being Solved: SQLite connection resource leaks when functions exit early due to exceptions or early returns. The old pattern manually called db.close() before each return, but if an unexpected error occurred mid-execution, connections would remain open.

Example from queryNameData():

// OLD: Manual db.close() before each return (❌ resource leak risk)
if (nodes.length === 0) {
  db.close();  // Must remember to close before every return
  return { query: name, results: [] };
}
// ... more code that could throw ...
db.close();  // Final close, but unreachable if exception occurs

// NEW: Guaranteed cleanup with try/finally (✅ always closes)
try {
  if (nodes.length === 0) {
    return { query: name, results: [] };  // Early return OK
  }
  // ... code that might throw ...
} finally {
  db.close();  // Always executes, even on exceptions
}

Why This Approach: Standard resource management pattern. Ensures database connections are always released regardless of how the function exits (normal return, early return, or exception).

Risk Assessment: LOW RISK - This is a defensive programming improvement that reduces resource leaks without changing functionality.

Phase 2: Configuration Change Detection ✅

No high-risk configuration changes detected:

  • ✅ No linting/formatting rule changes
  • ✅ No test configuration modifications
  • ✅ No build configuration changes
  • ✅ Only source code files modified (12 .js files)
  • ✅ No TypeScript config, package.json, or workflow changes

Phase 3: Test Integrity ✅

No test weakening detected:

  • ✅ No test files modified (0 files in tests/)
  • ✅ No test expectations changed
  • ✅ PR claims "All 280 integration tests pass"
  • ✅ Single focused commit with clear intent
  • ✅ No changes to test timeouts, coverage thresholds, or assertions

Phase 4: Code Quality Review ✅

Excellent refactoring implementation:

Pattern Consistency: Every modified function follows identical try { ... } finally { db.close(); } pattern
Complete Coverage: All 35 functions across 12 files consistently refactored
Correct Exception Handling: Finally blocks guarantee resource cleanup even on exceptions
Intentional Exceptions Documented:

No Functional Changes: Logic preserved, only resource management improved
No Double-Close Risk: Each db handle opened once, closed once in finally block
Backward Compatibility: Public APIs unchanged

No issues found:

  • No missing try blocks
  • No leaked db handles
  • No double-close scenarios
  • No inconsistent patterns

Root Cause Analysis

Problem Being Solved: SQLite connection resource leaks when functions exit via exceptions or early returns
Why This Approach: Industry-standard resource management with guaranteed cleanup via finally blocks
Risk Assessment: LOW RISK - Defensive improvement with no functional changes

Critical Concerns

None identified. This is a textbook example of proper resource management refactoring.

Final Recommendation

  • Rating: ⭐⭐⭐⭐ (4/5)
  • Action: APPROVE
  • Reasoning: High-quality refactoring that eliminates resource leak potential. Consistent implementation across 35 functions, zero functional changes, proper exception handling, and no configuration weakening. This is exactly the type of defensive programming improvement that reduces technical debt and improves system reliability.

@carlos-alm carlos-alm merged commit 127d459 into main Mar 9, 2026
17 checks passed
@carlos-alm carlos-alm deleted the refactor/try-finally-db-close branch March 9, 2026 04:16
@github-actions github-actions bot locked and limited conversation to collaborators Mar 9, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant