Architecture#
flowchart TD
P[Producer<br>Binomic client] -->|submit: direct| B[AsyncredisBroker<br>Redis Streams]
P -->|submit: delay| S[TaskScheduler]
P -.->|registered at startup: cron / interval| S
S -->|enqueued when due| B
B -->|consumer group / PEL| W1[Worker 0<br>subprocess]
B -->|consumer group / PEL| W2[Worker 1<br>subprocess]
W1 -.->|failure or timeout:<br>redeliver after a backoff, attempt + 1| B
M[Master<br>supervisor] -->|restart when dead| W1
M -->|restart when dead| W2
W1 -.->|presence heartbeat: IPC| M
W2 -.->|presence heartbeat: IPC| M
Components#
Producer:
Binomic.submitbranches on the task’s mode — adirecttask is enqueued immediately through the broker thatBrokerFactorycreates; adelaytask is handed to the scheduler for a deferred enqueue;cronandintervaltasks do not go throughsubmit(calling it on them raisesValueError) and are instead registered as periodic jobs when the client starts. Which stream a message lands in comes from the task declaration (TaskSpec.queue), not from the caller.Scheduler:
TaskSchedulercreates the matching trigger for each of thedelay/cron/intervalmodes, and enqueues through the same path asdirectonce a job is due. Each worker holds aTaskSchedulerof its own (unrelated to the client’s), which carries the backoff of a failed redelivery. See Task modes and scheduling.Broker:
AsyncredisBrokerwrites messages to Redis Streams; workers read them through a consumer group, and messages in the PEL of a lost consumer are reclaimed and redelivered byreclaim(xautoclaim), which bumpsattempt.Master / Worker: the master spawns worker subprocesses through multiprocessing and supervises their liveness. The heartbeat goes over inter-process IPC rather than Redis: the master creates one shared
multiprocessing.Valueper worker and hands it to the child as aWorkerconstructor argument, the child stamps a timestamp into it on every interval, and the master reads it directly to judge death. That keeps a process which is up but has stopped beating distinguishable from one that is gone – the caseis_alive()alone cannot see. Each worker executes tasks concurrently withanyioinside its process. A task that succeeds isacked; one that fails or overruns is redelivered after a backoff whilemax_attemptslasts (the wait is deferred through the worker’s own scheduler) and dropped after that; a cancelled task is notacked, leaving its message toreclaim. See Reliability.