fix(storage): close the connection when close()'s commit fails 🤖🤖🤖 - #296
fix(storage): close the connection when close()'s commit fails 🤖🤖🤖#296sushant-mishra-dtu wants to merge 1 commit into
Conversation
SQLiteStorageManager.close() sets self._closed = True before it commits,
then commits and closes inside the same statement pair:
self._closed = True
...
conn.commit()
conn.close()
If commit() raises -- a full disk, an I/O error, a locked database --
conn.close() is never reached. The connection is left open, and because
_closed is already True the guard at the top of close() makes every
later close() a no-op, so nothing can ever close it:
close() raised : disk I/O error
manager._closed : True
conn.close() called: False
after retry close(): False
__exit__ calls close(), so a `with SQLiteStorageManager(...)` block that
fails to commit on the way out leaks its connection and the file handle
under it, and the caller's own recovery close() cannot reclaim it.
Commit in a try / finally so the close always runs. The commit error
still propagates, unchanged.
🤖🤖🤖
Signed-off-by: sushant-mishra-dtu <sushant.arh@gmail.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Enterprise Run ID: 📒 Files selected for processing (2)
Included review availability: Your plan provides up to 12 included reviews per hour; 9 remain after this review. 📝 WalkthroughWalkthrough
ChangesSQLite connection cleanup
Estimated code review effort: 2 (Simple) | ~10 minutes Merge Risk: ⚪ Minimal · up to SQLite connections are now closed when commit fails while preserving the original error behavior. The targeted regression test covers this failure path, with no remaining merge-blocking risk identified. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
What this fixes
SQLiteStorageManager.close()marks itself closed before it does the closing(
src/nooa/storage/sqlite.py:859-878):sqlite3.Connection.commit()raises on a full disk, an I/O error, or a database anotherprocess holds. When it does,
conn.close()is skipped and the connection is left open --and because
self._closedis alreadyTrue, the guard on the first line turns everylater
close()into a no-op. Nothing can ever close it.Why it matters
__exit__is justself.close()(src/nooa/storage/sqlite.py:884), so this is reached bythe ordinary
with SQLiteStorageManager(...) as sm:form. A block that fails to commit onthe way out leaks the connection and the file handle beneath it, and the caller's own
recovery path cannot reclaim them --
sm.close()in anexcept/finallyreturnsimmediately, reporting success.
The
finally:block does release the advisory session lock, so this is not a deadlock. Itis a connection and file-descriptor leak with no recovery, in exactly the situation where a
caller is most likely to be retrying.
Reproduction
On
main:On this branch:
The fix
Commit in a
try/finallyso the close always runs, on both the locked and unlockedpaths:
The commit error still propagates unchanged -- the caller learns the commit failed, and now
the connection is closed either way. No signature change, no change on the success path.
Test
One test,
test_close_closes_the_connection_when_commit_fails, in the existingTestSQLiteStorageManagerclass. Verified to fail on the unfixed tree before being kept:Scope
Only the commit/close ordering. The
finally:block below it, which unlocks and closes_lock_fd, is untouched -- that is the part #132 and #135 change for Windows, and thisbranch deliberately stays out of their way.
Summary by CodeRabbit
Bug Fixes
Tests