fix(db): await AsyncConnection.execution_options in run_maintenance
execution_options() is a coroutine on AsyncConnection and must be awaited; the un-awaited call returned a coroutine, so exec_driver_sql() blew up with AttributeError and every table's VACUUM was skipped (Run-now reported 0/6). A prior change had wrongly dropped the await. Fix it and make the test mock execution_options async so this call shape is actually exercised. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -66,9 +66,10 @@ async def run_maintenance(tables: tuple[str, ...] | list[str] | None = None) ->
|
|||||||
results: list[dict] = []
|
results: list[dict] = []
|
||||||
|
|
||||||
async with engine.connect() as conn:
|
async with engine.connect() as conn:
|
||||||
# execution_options() is synchronous on AsyncConnection; it returns the
|
# On AsyncConnection, execution_options() is a coroutine and MUST be
|
||||||
# connection with AUTOCOMMIT set (VACUUM can't run in a transaction).
|
# awaited (it returns the connection with AUTOCOMMIT set — VACUUM can't
|
||||||
autocommit = conn.execution_options(isolation_level="AUTOCOMMIT")
|
# run inside a transaction block).
|
||||||
|
autocommit = await conn.execution_options(isolation_level="AUTOCOMMIT")
|
||||||
for table in targets:
|
for table in targets:
|
||||||
t0 = time.monotonic()
|
t0 = time.monotonic()
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -16,7 +16,9 @@ def _mock_engine(exec_side_effect=None):
|
|||||||
conn = MagicMock()
|
conn = MagicMock()
|
||||||
autocommit = MagicMock()
|
autocommit = MagicMock()
|
||||||
autocommit.exec_driver_sql = AsyncMock(side_effect=exec_side_effect)
|
autocommit.exec_driver_sql = AsyncMock(side_effect=exec_side_effect)
|
||||||
conn.execution_options.return_value = autocommit # sync on AsyncConnection
|
# On AsyncConnection, execution_options() is a coroutine — mock it as async
|
||||||
|
# so the test exercises the real (awaited) call shape.
|
||||||
|
conn.execution_options = AsyncMock(return_value=autocommit)
|
||||||
cm = AsyncMock()
|
cm = AsyncMock()
|
||||||
cm.__aenter__ = AsyncMock(return_value=conn)
|
cm.__aenter__ = AsyncMock(return_value=conn)
|
||||||
cm.__aexit__ = AsyncMock(return_value=False)
|
cm.__aexit__ = AsyncMock(return_value=False)
|
||||||
@@ -36,9 +38,9 @@ async def test_vacuums_each_allowlisted_table_once():
|
|||||||
assert issued == [f"VACUUM (ANALYZE) {t}" for t in MAINTENANCE_TABLES]
|
assert issued == [f"VACUUM (ANALYZE) {t}" for t in MAINTENANCE_TABLES]
|
||||||
assert all(r["ok"] for r in summary["tables"])
|
assert all(r["ok"] for r in summary["tables"])
|
||||||
assert len(summary["tables"]) == len(MAINTENANCE_TABLES)
|
assert len(summary["tables"]) == len(MAINTENANCE_TABLES)
|
||||||
# AUTOCOMMIT was requested — VACUUM cannot run inside a transaction.
|
# AUTOCOMMIT was requested (and awaited) — VACUUM can't run in a transaction.
|
||||||
conn = engine.connect.return_value.__aenter__.return_value
|
conn = engine.connect.return_value.__aenter__.return_value
|
||||||
conn.execution_options.assert_called_once_with(isolation_level="AUTOCOMMIT")
|
conn.execution_options.assert_awaited_once_with(isolation_level="AUTOCOMMIT")
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
|
|||||||
Reference in New Issue
Block a user