Skip to content

Coming from Dramatiq

Dramatiq and gylo agree on a lot: sane defaults, retries with backoff out of the box, lower priority number runs first. The differences are where the jobs live and what is built in rather than assembled from middleware.

The structural difference

actor.send() posts to RabbitMQ or Redis, outside your database transaction. gylo enqueues on a connection you pass, so the job commits atomically with the state change that caused it:

import gylo

app = gylo.Gylo()


@app.task
async def send_email(user_id: int) -> None: ...


async def register(conn, email: str) -> None:
    async with conn.transaction():
        user_id = await insert_user(conn, email)
        await send_email.enqueue(conn, user_id)

What Dramatiq composes from middleware — Retries, TimeLimit, Results, ShutdownNotifications — is core behaviour here: backoff with jitter by default, a 300s timeout on every task, store_result=True when you want the return value, and a SIGTERM drain that finishes in-flight work. There is no middleware stack to order correctly.

Concept map

Dramatiq gylo
@dramatiq.actor @app.task
actor.send(x) await task.enqueue(conn, x)
send_with_options(delay=60_000) — milliseconds task.options(delay=60).enqueue(conn, …) — seconds
queue_name="emails" options(queue="emails")
priority=0 runs before priority=10 same — lower number first
max_retries=3 options(max_attempts=4) — total attempts
min_backoff / max_backoff — per actor, milliseconds --retry-base / --retry-cap — worker-wide, seconds, jitter always on
throws=(ValueError,) no_retry_on=(ValueError,)
TimeLimit middleware timeout, on by default
Results middleware + result backend store_result=True, await gylo.outcome(conn, job_id)
message.get_result(block=True) poll outcome — nothing blocks
group gylo.group
pipeline gylo.chain — but see below
periodiq / APScheduler @app.cron("0 9 * * *"), runs inside the worker
RabbitMQ / Redis broker Postgres
processes × threads one Python process per core, coordinating through the queue

What gylo will not do the same way

Chains do not pipe results. A Dramatiq pipeline hands each actor's return value to the next; a gylo chain only orders execution — every step receives its own arguments. Pass work through your own tables. The reasoning is in Workflows.

No middleware hooks. Dramatiq's middleware API is a genuine extension point; gylo does not expose one. What the middlewares are usually for — retries, time limits, results, graceful shutdown, dead letters — is built in, but if you have a custom middleware doing something else, there is no equivalent seam today.

No rate limiter. Dramatiq ships backend-backed rate limiters; gylo's keyed concurrency bounds simultaneous jobs per key, not jobs per second.

Threads are not a knob. Dramatiq runs processes × threads; gylo runs one child process per core and dispatches the async event loop inside each. Synchronous tasks run on a thread pool automatically — but concurrency is configured as one number, not two.

Results expire with the job row. The retrieval window is the completed retention window, 24 hours by default. Results that must outlive it belong in your own tables — see Results and cancellation.