Fixes the flood of recompute_centroid failures in the activity view: RuntimeError: ... got Future ... attached to a different loop.
Root cause
recompute_centroid + recompute_centroids were the only tasks still using the process-wide singleton extensions.get_session() under asyncio.run(). The async engine's asyncpg pool is bound to the loop it was created on; each Celery task runs a fresh loop, so after the first invocation the cached engine handed old-loop connections to the new loop → fails on the first DB await (~35ms).
Fix
Convert both to the established per-task async_session_factory() pattern (NullPool engine created + disposed inside the task's own loop), matching scan/download/admin tasks. No get_session usages remain in tasks/.
CI green on dev (run 1179, 51201b4). Deploy note: this runs on the ml queue → re-pull the ml-worker image.
Fixes the flood of `recompute_centroid` failures in the activity view: `RuntimeError: ... got Future ... attached to a different loop`.
## Root cause
`recompute_centroid` + `recompute_centroids` were the only tasks still using the process-wide singleton `extensions.get_session()` under `asyncio.run()`. The async engine's asyncpg pool is bound to the loop it was created on; each Celery task runs a fresh loop, so after the first invocation the cached engine handed old-loop connections to the new loop → fails on the first DB await (~35ms).
## Fix
Convert both to the established per-task `async_session_factory()` pattern (NullPool engine created + disposed inside the task's own loop), matching scan/download/admin tasks. No `get_session` usages remain in `tasks/`.
CI green on dev (run 1179, 51201b4). Deploy note: this runs on the `ml` queue → re-pull the **ml-worker** image.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
recompute_centroid + recompute_centroids were the only tasks still using
the process-wide singleton extensions.get_session() under asyncio.run().
The async engine's asyncpg pool is bound to the loop it was created on;
each Celery task runs a fresh asyncio.run() loop, so after the first
invocation the cached engine handed loop-A connections to loop B and raised
"Future attached to a different loop" — every recompute after the first in
a worker process failed (~35ms, fails on first DB await).
Convert both to the established per-task async_session_factory() pattern
(NullPool engine created + disposed inside the task's own loop), matching
scan/download/admin tasks. No get_session usages remain in tasks/.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Fixes the flood of
recompute_centroidfailures in the activity view:RuntimeError: ... got Future ... attached to a different loop.Root cause
recompute_centroid+recompute_centroidswere the only tasks still using the process-wide singletonextensions.get_session()underasyncio.run(). The async engine's asyncpg pool is bound to the loop it was created on; each Celery task runs a fresh loop, so after the first invocation the cached engine handed old-loop connections to the new loop → fails on the first DB await (~35ms).Fix
Convert both to the established per-task
async_session_factory()pattern (NullPool engine created + disposed inside the task's own loop), matching scan/download/admin tasks. Noget_sessionusages remain intasks/.CI green on dev (run 1179,
51201b4). Deploy note: this runs on themlqueue → re-pull the ml-worker image.🤖 Generated with Claude Code