Background workers¶
Add background processes to a PHP application with addWorker():
(new SymfonyService('app'))
->withDirectory(__DIR__)
->addWorker('messenger', 'php bin/console messenger:consume async --time-limit=3600', 'unless-stopped')
->addWorker('notifications', 'php bin/console app:process-notifications')
The third argument is the compose restart policy of that container —
unless-stopped, on-failure, always, no. There is none by default: a
worker that exits stays down until the next castor docker:up.
Each worker runs in its own container, named app-worker-{name}, built from the
worker target of the application image. Workers share the application's
volumes, database and mailer configuration, so DATABASE_URL and MAILER_DSN
are available to them too.
Driving them¶
An application that declares workers gets two tasks for them — they do not exist otherwise:
castor app:worker:restart # every worker of the application
castor app:worker:restart messenger # just that one
castor app:worker:stop # every worker
castor app:worker:stop notifications # just that one
The argument is the name you passed to addWorker(), not the container name, and
an unknown one is rejected with the list of those declared — rather than
quietly acting on all of them.
worker:restart starts a worker that was stopped, so there is no separate start
task: stop then restart is the round trip. After a code change, restarting is
what picks it up:
castor app:install && castor app:worker:restart
The containers stay reachable by name for everything else:
castor docker:logs app-worker-messenger
Keeping a consumer alive¶
Give your consumers a --time-limit or a --memory-limit: a long-running PHP
process accumulates memory, and letting it stop on its own terms beats letting
it grow.
That only keeps them running if something brings the container back, and that is the restart policy:
->addWorker('messenger', 'php bin/console messenger:consume async --time-limit=3600', 'unless-stopped')
Important
Reaching a --time-limit or a --memory-limit is a successful exit, so
on-failure does not bring the worker back — it only reacts to a non-zero
one. Use unless-stopped for a consumer meant to run forever.
unless-stopped rather than always: it honours castor app:worker:stop,
where always would start the container again the next time the Docker daemon
does.