Comparison
This table compares VarMQ with ants, pond v2, conc, workerpool, errgroup and gopool. The cells are the same as in the feature comparison in the README. The notes marked VarMQ limit are new here: they say where a VarMQ Yes has a limit.
How to read it. Every row is a feature VarMQ has, so the VarMQ column reads Yes all the way down. The table does not list features that another library has and VarMQ lacks. Where a VarMQ Yes has a limit, the row has a VarMQ limit note, with the page of these docs that explains it. For speed, see the benchmarks.
| Feature | varmq | ants | pond | conc | workerpool | errgroup | gopool |
|---|---|---|---|---|---|---|---|
| Generic (type-safe) API | Yes | Yes | Yes | Yes | No | No | No |
| Tasks return results and errorsVarMQ limit Only on in-memory queues. On a persistent or distributed queue, Add returns a bool and no job handle, so there is nothing to Wait on and no Err or Result to read. Worker types | Yes | No | Yes | Yes | No | No2 | No |
| Task groups (submit many, wait once)VarMQ limit Only on in-memory queues. A persistent or distributed queue has no AddAll. Persistence overview | Yes | No | Yes | Yes | No | Yes | No |
| Priority queue | Yes | No | No | No | No | No | No |
| Persistent queues (survive restarts)VarMQ limit VarMQ has no adapter of its own: you add redisq (Redis) or sqliteq (SQLite). The best case is at-least-once delivery, so a job can run twice. What this guarantees | Yes | No | No | No | No | No | No |
| Distributed queues (across instances)VarMQ limit Of the existing adapters, only redisq (Redis) provides one. sqliteq has no distributed queue. An adapter of your own can implement IDistributedQueue. SQLite | Yes | No | No | No | No | No | No |
| Several queues bound to one pool | Yes | No | Yes4 | No | No | No | No |
| Topic fan-out (publish once, many pools)VarMQ limit Topics are in-memory only. To share jobs between processes, use a distributed queue. Topics | Yes | No | No | No | No | No | No |
| Bounded queue, reject when fullVarMQ limit Not with redisq or sqliteq. Neither implements CapacitySetter, so Add is not refused on their persistent or distributed queues. An adapter of your own can implement it. Persistence overview | Yes | Yes | Yes | No | No | No3 | No |
| Pause and resumeVarMQ limit On a persistent or distributed queue, Pause and Stop do not park a job the worker already took. It runs. Acknowledgement | Yes | No | No | No | Yes | No | No |
| Restart after stop | Yes | Yes | No | No | No | No | No |
| Resize pool at runtime | Yes | Yes | Yes | No | No | No | Yes |
| Cancel work via contextVarMQ limit Cancelling the context shuts the worker down. A running job is not interrupted: VarMQ cannot stop a function. Jobs that wait do not start. Context | Yes | No | Yes | Yes | No | Yes | No |
| Panic recovery | Yes | Yes | Yes | No1 | No | No | Yes |
| Task counters (submitted, failed, ...) | Yes | No | Yes | No | No | No | No |
| Built-in REST API | Yes | No | No | No | No | No | No |
No row matches.
- conc re-raises a task’s panic from
Waitby design. - errgroup returns the first error, no results.
- errgroup has no queue;
TryGoonly refuses a task while every slot is busy. - pond subpools: each has its own queue and runs on the parent pool’s workers.
Checked against each package’s source, October 2026.
What this page leaves out
This table covers in-process worker pools only. The benchmarks also measure Redis job queues (Asynq, gocraft/work, rmq, taskq) and SQLite job queues (goqite, Backlite, River). This page has no feature table for them.
For how fast each one runs, see the benchmarks, with the raw data in goptics/benchmarks, and the bench log.