we dont close on GC connections that were detached (and weren't closed). should we?
Maintainer antworten meist innerhalb von 1 Tag
Dieses Issue hat noch niemand übernommen.
Bewertung
- Schwierigkeit
- 4/5
- Geschätzter Aufwand
- 3-5 Tage
- Anfängerfreundlichkeit
- 48/100
Rechercherichtung
Lies _ConnectionFairy.detach(), _finalize_fairy() und die im Bericht erwähnte Einrichtung von weakref.finalize. Verwende die bereitgestellten synchronen und asyncio-Reproduktionen, um das Verhalten zu überprüfen. Die Aufgabe gilt als abgeschlossen, wenn getrennte Verbindungen während der Garbage Collection geschlossen werden und die asyncio-Warnung zur Garbage Collection einheitlich behandelt wird.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Beschreibung
Describe the bug
A connection that has been detached from its pool with
Connection.detach() / PoolProxiedConnection.detach() and is then
garbage collected without being explicitly closed never has its DBAPI
connection closed. The connection is leaked until the DBAPI itself
reclaims it, if it ever does.
Detaching hands ownership of the DBAPI connection to the fairy, and the
explicit close path handles this correctly: _ConnectionFairy._checkin()
calls _finalize_fairy() with connection_record=None, which takes the
detach = connection_record is None branch and calls
pool._close_connection().
The garbage collection path never reaches that branch. The finalizer
established for the fairy at checkout closes over the record, and
_ConnectionFairy.detach() clears _ConnectionRecord.fairy_ref, so when
the fairy is later collected the cleanup is skipped entirely. The
connection is not closed, and for asyncio dialects the
"garbage collector is trying to clean up non-checked-in connection"
warning that a non-detached abandoned connection would produce is not
emitted either.
This is long standing behavior, reproduced unchanged on 1.4, 2.0 and
current main.
Optional link from https://docs.sqlalchemy.org which documents the behavior that is expected
https://docs.sqlalchemy.org/en/20/core/connections.html#sqlalchemy.engine.Connection.detach
SQLAlchemy Version in Use
2.1.0b4, also 2.0 and 1.4
DBAPI (i.e. the database driver)
sqlite3, aiosqlite
Database Vendor and Major Version
SQLite
Python Version
3.14
Operating system
Linux
To Reproduce
import gc
import sqlite3
from sqlalchemy import create_engine
from sqlalchemy import text
closed = []
class TrackingConnection:
"""wrap a DBAPI connection so we can see close() being called"""
def __init__(self, real):
self.real = real
def close(self):
closed.append(self)
self.real.close()
def __getattr__(self, name):
return getattr(self.real, name)
def creator():
return TrackingConnection(sqlite3.connect(":memory:"))
engine = create_engine("sqlite://", creator=creator)
# 1. detach, then close explicitly -> DBAPI connection is closed
conn = engine.connect()
conn.execute(text("select 1"))
conn.detach()
conn.close()
print("detach() + close() -> DBAPI close() called:", len(closed) == 1)
# 2. detach, then just drop it -> DBAPI connection is leaked
closed.clear()
conn = engine.connect()
conn.execute(text("select 1"))
conn.detach()
del conn
gc.collect()
print("detach() + gc -> DBAPI close() called:", len(closed) == 1)
The asyncio equivalent, showing that the GC warning is also skipped for a
detached connection:
import asyncio
import gc
import warnings
from sqlalchemy import text
from sqlalchemy.ext.asyncio import create_async_engine
async def main():
e = create_async_engine("sqlite+aiosqlite://")
for label, do_detach in (("not detached", False), ("detached", True)):
conn = await e.connect()
await conn.execute(text("select 1"))
raw = await conn.get_raw_connection()
if do_detach:
raw.detach()
del conn, raw
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
gc.collect()
msgs = [
str(x.message) for x in w if "garbage collector" in str(x.message)
]
print(f" {label:14s} -> gc warning emitted: {bool(msgs)}")
asyncio.run(main())
Error
detach() + close() -> DBAPI close() called: True
detach() + gc -> DBAPI close() called: False
not detached -> gc warning emitted: True
detached -> gc warning emitted: False
Additional context
Noticed while reviewing
https://gerrit.sqlalchemy.org/c/sqlalchemy/sqlalchemy/+/6932, which
converts the fairy's garbage collection cleanup to weakref.finalize().
That change does not alter this behavior, it only makes it more visible:
where the previous code left a weakref callback in place that returned
early, _ConnectionFairy.detach() now cancels the finalizer outright.
A fix would need the detached fairy to keep a finalizer that closes over
connection_record=None, so that collection takes the same
detach/_close_connection() branch that the explicit close path takes.
This is not a two line change: _finalize_fairy() asserts a non-null
record in two places, and the second of those builds a throwaway
_ConnectionFairy to run the reset with, which currently requires a
record.
- Vorherrschende Sprache
- Python
- Sterne
- 12.3k
- Forks
- 1.8k
- Ø Merge
- 2 T. 10 Std.
- Gemergte PRs (30 T.)
- 9
Entwicklungsumgebung
- Kein Dockerfile und keine Docker-Compose-Datei
- Hat eine Pull-Request-Vorlage
- Beitragsleitfaden lesen
Erste Schritte
- Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
- Forken Sie das Repository und arbeiten Sie in einem Branch.
- Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.
Mehr aus sqlalchemy/sqlalchemy
-
requires triage
Schwierigkeit 1/5 1-3 Stunden Anfängerfreundlichkeit 85/100
sqlalchemy/sqlalchemy#13784 · 1 Kommentar ·
Maintainer antworten meist innerhalb von 1 Tag
-
bug engine
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 88/100
sqlalchemy/sqlalchemy#13770 · 2 Kommentare ·
Maintainer antworten meist innerhalb von 1 Tag
-
declared_attr.cascading fails to typecheck under mypy / pyright on documented usageEvtl. wieder frei Ein Pull Request dazu wurde ohne Merge geschlossen. Offenbug orm typing
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 76/100
sqlalchemy/sqlalchemy#13731 · 3 Kommentare ·
Maintainer antworten meist innerhalb von 1 Tag
-
PostgreSQL multihost URL with bracketed IPv6 hosts raises ValueError: too many values to unpackEvtl. wieder frei Ein Pull Request dazu wurde ohne Merge geschlossen. Offenbug engine postgresql
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 88/100
sqlalchemy/sqlalchemy#13637 · 6 Kommentare ·
Maintainer antworten meist innerhalb von 1 Tag
-
asyncpg dialect raises DBAPIError, not DataError, for SQLSTATE class 22 errorsEvtl. vergeben @HardMax71 hat das vor 20 Tagen übernommen. Offenbug code review in progress dialects
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 84/100
sqlalchemy/sqlalchemy#13636 · 19 Kommentare ·
Maintainer antworten meist innerhalb von 1 Tag
Alle Issues in sqlalchemy/sqlalchemy
Ähnliche Issues
-
Unknown stop ID in slow zone data crashes post formatting and skips the rest of that day's postsOffenbackend bug tm-new-volunteer
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 84/100
transitmatters/mbta-slow-zone-bot#81 · 1 Kommentar ·
-
area:schema documentation
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 76/100
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 72/100
Maintainer antworten meist innerhalb von 1 Tag
-
risk:high type:ci
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 76/100
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 64/100
spacetelescope/rdmt-spire#49 ·
Maintainer antworten meist innerhalb von 1 Tag