Skip to content

Commit c580bc5

Browse files
Attach drain-observer when invalidate cancels prior pending drain
connection.py:1740-1742 cancelled the prior _pending_drain task and overwrote the slot WITHOUT attaching _observe_drain_exception. Every other cancel-and-detach site in the package (cluster.py:1134, cluster.py:1863, connection.py:1450-1453) attaches the observer to consume any non-CancelledError via task.exception() — without it, asyncio's task-finalisation logger emits "Task exception was never retrieved" at GC. In practice _bounded_drain swallows Exception so today's orphan only carries CancelledError (silent). The fix is INSURANCE for future refactors that loosen the suppress scope. Symmetric discipline with the four sibling sites. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent d0dbd94 commit c580bc5

1 file changed

Lines changed: 13 additions & 0 deletions

File tree

src/dqliteclient/connection.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1739,6 +1739,19 @@ async def _bounded_drain() -> None:
17391739
# parity.
17401740
prior = self._pending_drain
17411741
if prior is not None and not prior.done():
1742+
# Attach the drain-observer BEFORE cancelling so a
1743+
# non-CancelledError raised by the prior task
1744+
# (e.g. a future regression in ``_bounded_drain``
1745+
# that lets an exception escape) is consumed via
1746+
# ``task.exception()``. Without the observer,
1747+
# asyncio's task-finalisation logger emits
1748+
# "Task exception was never retrieved" at GC.
1749+
# Mirrors the cluster.py:1134 / cluster.py:1863 /
1750+
# connection.py:1450-1453 sibling sites and the
1751+
# dbapi's ``_cancel_and_observe`` pattern.
1752+
from dqliteclient.cluster import _observe_drain_exception
1753+
1754+
prior.add_done_callback(_observe_drain_exception)
17421755
prior.cancel()
17431756
# Strong-ref on self so the task is not GC'd before
17441757
# close() awaits it.

0 commit comments

Comments
 (0)