Summary
DbWriter.TransactionScope has a savepoint constructor (line 71) that assigns _conn. Commit() (line 83) and Dispose() (line 111) execute RELEASE/ROLLBACK SAVEPOINT via _conn! — the null-forgiving operator suggests the author knows nullability is in play but trusts the constructor. The catch block in Dispose (line 113) silently swallows any exception, including a NullReferenceException if _conn is somehow not initialized. The combination — null-forgiving deref + silent catch — turns a constructor-init bug into a quiet "transaction never released" leak that's invisible from logs.
Where
src/CodeIndex/Database/DbWriter.cs:71-96 (TransactionScope savepoint constructor)
src/CodeIndex/Database/DbWriter.cs:111-125 (Dispose with silent catch)
Suggested approach
(1) Replace _conn! with explicit if (_conn is null) throw new InvalidOperationException(...) so the bug surfaces at the failure point rather than being eaten. (2) Have the Dispose catch block log the exception (to GlobalToolLog) before swallowing — silent swallow is fine for cleanup, but the log breadcrumb makes debugging possible. (3) Add a regression test that constructs a partial TransactionScope (via reflection or a test helper) with _conn = null and asserts the failure is observable. (4) Document the lifecycle invariants in the class XML doc.
Summary
DbWriter.TransactionScopehas a savepoint constructor (line 71) that assigns_conn.Commit()(line 83) andDispose()(line 111) executeRELEASE/ROLLBACK SAVEPOINTvia_conn!— the null-forgiving operator suggests the author knows nullability is in play but trusts the constructor. The catch block inDispose(line 113) silently swallows any exception, including aNullReferenceExceptionif_connis somehow not initialized. The combination — null-forgiving deref + silent catch — turns a constructor-init bug into a quiet "transaction never released" leak that's invisible from logs.Where
src/CodeIndex/Database/DbWriter.cs:71-96(TransactionScope savepoint constructor)src/CodeIndex/Database/DbWriter.cs:111-125(Dispose with silent catch)Suggested approach
(1) Replace
_conn!with explicitif (_conn is null) throw new InvalidOperationException(...)so the bug surfaces at the failure point rather than being eaten. (2) Have the Dispose catch block log the exception (to GlobalToolLog) before swallowing — silent swallow is fine for cleanup, but the log breadcrumb makes debugging possible. (3) Add a regression test that constructs a partial TransactionScope (via reflection or a test helper) with_conn = nulland asserts the failure is observable. (4) Document the lifecycle invariants in the class XML doc.