Lease proxies for write and isolated callbacks; close() never frees a busy connection

Callbacks passed to execute_write_fn() - and the internal callbacks of
execute_write(), execute_write_many() and execute_write_script(), plus
write_wrapper hooks - and execute_isolated_fn() now receive a
LeasedConnection that raises ConnectionLeaseError once the callback has
returned, in threaded and non-threaded mode. "with conn as c" yields the
proxy, not the raw connection. Write callbacks may still return a cursor
(lambda conn: conn.execute(...) is common, and the write_many/script
methods return theirs).

The threaded execute_isolated_fn() path for immutable databases now runs
as a tracked executor future, so Database.close() waits for it instead
of closing its connection mid-query (the segfault in the review, P03).
close() also leaves alone the connections of a write thread that is
still running after the 10s join; that thread closes them itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwkLo2rJWidpkDVT3HkvNv
This commit is contained in:
Claude 2026-10-04 04:51:51 +00:00
commit 5c30d14d3b
No known key found for this signature in database
3 changed files with 257 additions and 20 deletions

View file

@ -1319,9 +1319,14 @@ async def test_close_releases_memory_connections(num_sql_threads, named):
read_connection = await db.execute_fn(
lambda conn: conn.execute("select 1").connection
)
write_connection = await db.execute_write_fn(lambda conn: conn)
leased_write = await db.execute_write_fn(lambda conn: conn)
write_connection = await db.execute_write_fn(
lambda conn: conn.execute("select 1").connection
)
with pytest.raises(ConnectionLeaseError):
leased.execute("select 1")
with pytest.raises(ConnectionLeaseError):
leased_write.execute("select 1")
ds.close()
for conn in (read_connection, write_connection):
with pytest.raises(sqlite3.ProgrammingError, match="closed"):