Skip to content

fix: act on worker signals only for the app that owns the container - #44

Merged
lesnik512 merged 2 commits into
mainfrom
fix/worker-signals-own-app
Oct 17, 2026
Merged

lesnik512 merged 2 commits into
mainfrom
fix/worker-signals-own-app

Conversation

@lesnik512

@lesnik512 lesnik512 commented Oct 17, 2026 •

Copy link
Copy Markdown
Member

Closes #43

setup_di connected its handlers to the four worker signals with no filter, so in a process with two apps one worker's signals opened and closed both containers. Each handler now checks that its app owns the container before acting.

  • worker_init and worker_shutdown: Celery sends these with the WorkController as sender (celery/worker/worker.py). The handler reads sender.app, resolves a proxy to the real app, and acts only when that app's root container is this one. A sender with no app falls back to the current app, which keeps the existing sender=None test working.
  • worker_process_init and worker_process_shutdown: Celery sends these with sender=None (celery/concurrency/prefork.py, solo.py). Under prefork they run in the child after process_initializer has made the worker's app current; under solo, worker_process_init runs in the main process, where the current app is the worker's app unless it was created with set_as_current=False. The handler checks the current app. The init handler still binds its container to a spawn child's unpickled copy by main first, then checks ownership, so the spawn path works as before.

Ownership is a lookup in the existing _ROOT_CONTAINERS registry, so an app that already has a container is never claimed by another app with the same main.

One behavior change for a single app: under the solo pool, an app created with set_as_current=False that is not current no longer opens on worker_process_init. It still opens on worker_init, which every pool sends with the worker as sender.

The binding step moved to a module-level helper to keep setup_di under ruff's complexity limit. The _restore_current_app fixture moved from tests/test_spawn.py to tests/conftest.py so the new tests can change the current app without leaking it.

Tests: two new tests in tests/test_lifespan.py, one per signal pair, failed on main and pass now. I also ran the repro from the issue and a real WorkController with the threads pool: only the owning app's container opens and closes.

@lesnik512
lesnik512 merged commit 2a12b68 into main Oct 17, 2026
12 checks passed
@lesnik512
lesnik512 deleted the fix/worker-signals-own-app branch October 17, 2026 12:33
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.

One app's worker signals open and close every app's container in the process

1 participant