Python task queues compared¶
Python has a crowded field of task queues, and picking one is mostly about matching a
library to how your service is shaped. This page lays out the landscape honestly, with
real download data, and explains where aio-fluid does, and does not, make sense.
If you are choosing for the first time, the short version:
- You want the biggest ecosystem and battle-tested defaults: Celery.
- You want something simple and Redis-only: RQ.
- You run an async (
asyncio) service and your tasks never block the loop: an async-native queue like arq or taskiq. - You run an async service and some tasks are CPU-heavy (parsing, pandas, model
scoring, rendering) that would otherwise freeze your event loop: this is exactly what
aio-fluidis built for. See CPU bound tasks and K8s Jobs.
Popularity¶
Is Celery still the one everyone uses? Yes, decisively. It out-downloads the nearest real
queue (RQ) by roughly an order of magnitude, and the async-native niche that aio-fluid
competes in is comparatively small. That is both the opportunity (few libraries own
"async-native + CPU-bound") and an honest reality check on the size of that audience.
Downloads = last 30 days on PyPI, via pypistats.org. Seeded figures; run make stats to refresh and re-rank from live data. Counts are inflated by CI, mirrors and Docker builds, so read them as orders of magnitude, not user counts.
| Library | Downloads / mo | Async-native | CPU work off the loop | Notes |
|---|---|---|---|---|
| Celery | 48.9M | partial | separate worker fleet | The incumbent: biggest ecosystem, broker-agnostic. |
| APScheduler | 37.1M | ✅ | n/a | A scheduler, not a distributed queue, listed for scale. |
| RQ | 2.4M | no | separate worker | Simple, Redis-only; the common 'lite Celery'. |
| Dramatiq | 270k | no | separate worker | Ergonomic Celery alternative. |
| Huey | 117k | no | separate worker | Lightweight, very few dependencies. |
| taskiq | 34k | ✅ | no | Async-native, pluggable brokers; typed parameters. |
| arq | n/a | ✅ | no | Async-native, Redis; count pending first make stats. |
| SAQ | n/a | ✅ | no | Async-native, Redis; count pending first make stats. |
| Procrastinate | n/a | ✅ | no | Async-native, Postgres-backed; count pending first make stats. |
| aio-fluid | 4k | ✅ | subprocess / k8s Job | This library: CPU-bound work is a first-class task type. |
The libraries¶
Celery is the default answer for a reason: the largest ecosystem, broker flexibility
(RabbitMQ, Redis, SQS…), and years of production hardening. It predates asyncio, though,
and its standard answer to CPU-bound work is to run a separate worker fleet. If you need
breadth and maturity, reach for Celery.
RQ is the "simple Celery": Redis-only, small API, easy to reason about. Synchronous by design, so it is great for straightforward background jobs but not aimed at async services.
Dramatiq is an ergonomic, well-designed Celery alternative with a cleaner API and retries/middleware built in. Still a worker-per-process model rather than async-native.
Huey is deliberately tiny and dependency-light, a good fit for small projects that want a queue without operational weight.
arq, taskiq, SAQ and Procrastinate are the async-native cohort. They run
async def tasks on the event loop and are excellent for IO-bound work. By design they
assume tasks do not block the loop, so CPU-bound work is out of scope. That is the same
limitation that motivated aio-fluid.
APScheduler is included only for scale: it is a scheduler (fire a job on an interval or cron), not a broker-backed distributed queue, so it solves a different problem.
Where aio-fluid fits¶
aio-fluid is an async-native queue like arq/taskiq, but it treats CPU-bound work as a
first-class task type instead of assuming it away. Mark a task
cpu_bound=True and it runs in a fresh
subprocess so heavy work never blocks the event loop; when the consumer runs inside
Kubernetes, the same task dispatches as a Kubernetes Job instead,
with no code change and no parallel worker deployment to maintain.
So the honest positioning is not "Celery killer." It is this: if you run an async service
and have ever watched one CPU-heavy task freeze the whole thing, aio-fluid is built for
that specific pain. If you have no CPU-bound work, a lighter async queue will serve you
just as well; if you need Celery's ecosystem, use Celery.
Caveats on the numbers¶
PyPI download counts are a rough popularity proxy, not a user count. They are inflated by
CI/CD pipelines, Docker image builds, and mirrors, and libraries that are transitive
dependencies of popular packages (Celery, APScheduler) are inflated the most. Treat every
figure above as an order of magnitude. A value of n/a means the count has not been
fetched yet.
Refreshing this table¶
The table above is regenerated from live data by scripts/task_queue_stats.py:
It fetches last-30-days downloads from the pypistats.org API for
the curated library list, re-ranks the table, and rewrites the region between the STATS
marker comments in this page. Run it before a release, or whenever the numbers look stale.