Quick start#
Installation#
# uv
uv add binomic
# pip
pip install binomic
Declare tasks#
Declare tasks in a tasks.py module inside your application package (the
worker discovers every tasks.py module through autodiscover):
# myapp/tasks.py
import time
from binomic.task import task
@task("default")
def example(index: int = 0) -> None:
print(f"[{index}] Current time: {time.time()}")
@task("default", mode="delay", delay=10) # runs after a 10-second delay
def delayed() -> None: ...
@task("default", mode="cron", cron="*/5 * * * *") # runs every 5 minutes
def periodic() -> None: ...
@task("default", mode="interval", interval=30) # runs every 30 seconds
def poll() -> None: ...
The first positional argument is the queue name and must match a queue in
the client’s queues configuration. Tasks are registered under the
lowercase form of the function name (example, delayed, periodic, and
poll above), which is how you refer to a task when submitting it.
Functions in the two periodic modes, cron and interval, take no
arguments — a periodic firing has no call arguments to pass, so declaring
one raises ValueError outright. See
Task modes and scheduling for the full description of all
four modes.
Start and submit#
from binomic.client import Binomic, BinomicFactory
from binomic.config import BinomicConfig
from binomic.message import Message
from binomic.task import autodiscover
# Discover and register every tasks.py module inside the myapp package
autodiscover("myapp")
factory = BinomicFactory(
broker_dsn="redis://localhost:6379/0",
module_name="myapp",
config=BinomicConfig(queues=["default"], workers=2, concurrency=5),
)
binomic: Binomic = factory.create()
# Entering the context starts the master (spawning worker subprocesses)
# and exposes the submission entry point
async with binomic:
await binomic.submit(Message(name="example", args=[1]))
broker_dsnis the only Redis connection you supply: both delivery and consumption go through the Redis Streams it points at, while the master’s liveness supervision of workers goes over inter-process IPC and needs no Redis;in
BinomicConfig,workersis the number of worker subprocesses the master spawns,concurrencyis the number of tasks each worker runs concurrently, andmax_attemptsis the attempt budget for a single message — see Reliability for its semantics;a
Message’snameis the task name andargs/kwargscarry the call arguments (the example above callsexamplewithindex=1). Which stream a message lands in is decided by the queue in the task declaration — a message neither needs nor can specify a queue;enqueued_atneeds no manual assignment: the client writes it – onto the message’s stream entry, not theMessageitself – at the moment of actual delivery, which is also why delay and periodic tasks see the current time on every delivery.