Background Workers

Updated Oct 15, 2026
  • Use WorkerController to start/stop async tasks during ASGI lifespan.
  • Prefer @app.lifespan to wire startup/shutdown; workers can depend on the same DI container used for WebSocket resources.
  • If start() fails while creating or scheduling a worker, it closes any rejected coroutine, cancels and awaits tasks already scheduled, resets the controller and re-raises the original error. The same controller can then be started again. Rollback failures are attached to the original error as notes.
from falcon_pachinko import WorkerController, worker

controller = WorkerController()


@worker
async def broadcaster(*, conn_mgr):
    async for ws in conn_mgr.connections():
        await ws.send_media({"type": "ping"})


@app.lifespan
async def lifespan(app):
    await controller.start(broadcaster, conn_mgr=app.ws_connection_manager)
    yield
    await controller.stop()

The @worker decorator is optional and marks intent only. WorkerController.start() runs only the workers that the application passes to it. An undecorated async function works in the same way.