Skip to content

fix: open root container for non-forking worker pools (3.0.1)#7

Merged
lesnik512 merged 3 commits into
mainfrom
fix/open-root-all-pools
Jul 21, 2026
Merged

fix: open root container for non-forking worker pools (3.0.1)#7
lesnik512 merged 3 commits into
mainfrom
fix/open-root-all-pools

Conversation

@lesnik512

Copy link
Copy Markdown
Member

Summary

  • setup_di opened the root container only via worker_process_init (closed via worker_process_shutdown). Celery sends worker_process_init only from the prefork and solo pools — verified against celery/concurrency/prefork.py and solo.py.
  • The gevent, eventlet, and threads pools run tasks in the main worker process and never send worker_process_init, so under those pools the root container was never opened, and modern-di 3.0's mandatory-open lifecycle made every @inject task raise ContainerClosedError.
  • The 3.0.0 test suite missed this: it runs under task_always_eager and opens the root container manually in conftest.py, so the signal wiring itself had zero coverage.
  • Fix: connect the existing _open_container/_close_container closures to worker_init/worker_shutdown in addition to the existing worker_process_init/worker_process_shutdown pair (unchanged). worker_init/worker_shutdown fire once in the main worker process for all pools, covering gevent/eventlet/threads; worker_process_init/worker_process_shutdown still fire per forked child (prefork) and in solo. The overlap is harmless — Container.open() no-ops when already open, close_sync() no-ops when nothing was cached.

Patch release 3.0.1, no public API change.

Test plan

  • New regression test (tests/test_lifespan.py::test_worker_init_opens_container_for_non_forking_pools) fires worker_init directly (without worker_process_init), simulating gevent/eventlet/threads — fails without the fix, passes with it.
  • just test-ci — full suite green, 100% line coverage.
  • just lint-ci — clean (ruff, ty, eof-fixer, check-planning).
  • architecture/dependency-injection.md updated to describe both signal pairs; planning/changes/2026-07-21.01-open-root-all-pools.md and planning/releases/3.0.1.md added.

🤖 Generated with Claude Code

setup_di opened the root container only via worker_process_init, which
Celery sends only from the prefork and solo pools. The gevent, eventlet,
and threads pools run tasks in the main worker process and never send
it, so under those pools the root container was never opened and every
@Inject task raised ContainerClosedError.

Connect the same open/close handlers to worker_init/worker_shutdown,
which fire once in the main worker process for all pools. The existing
worker_process_init/worker_process_shutdown pair is unchanged, still
giving each forked process (prefork) its own open/close cycle.
Simulates the gevent/eventlet/threads path: build a fresh app and
container, fire worker_init directly (never worker_process_init), and
assert the root container opens and an @Inject task resolves. Fails
without the fix, since only worker_process_init was connected before.
Release notes for the non-forking worker pool fix, plus the
lightweight-lane planning change file recording the defect and fix.
@lesnik512
lesnik512 merged commit ba2eeb7 into main Jul 21, 2026
6 checks passed
@lesnik512
lesnik512 deleted the fix/open-root-all-pools branch July 21, 2026 07:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant