Architecture
One daemon, three role-based endpoints, one allocator. Every moving piece is visible in a single diagram — and intentionally, no piece is optional.
Quicx is deliberately flat. A single daemonprocess owns the task queue, the worker pool and the PMAD allocator. Producers and workers are plain TCP clients that speak the same binary protocol — the first frame they send tells the daemon which role they’re playing.
There are four horizontal message paths:
- producer → daemon
MSG_SUBMITlands a new task. The daemon responds with eitherMSG_OK {task_id}orMSG_ERROR {code}— always, within one round-trip.- daemon → worker
- Workers announce themselves with
MSG_READYand block reading. The daemon pushesMSG_TASKframes to the first idle worker as soon as one is queued. If the queue is empty, the worker simply stays blocked —MSG_WAITis defined on the wire but nothing in the daemon actually sends it. - worker → daemon
MSG_DONE {task_id}on success,MSG_FAILED {task_id, reason}on failure.MSG_HEARTBEAT/MSG_PONGkeep the socket from half-closing under long idle.- daemon → producer
- As of v1.0.3, the daemon forwards the worker’s outcome back over the producer’s original connection —
MSG_DONE {task_id}orMSG_FAILED {task_id, reason}with the worker’s real reason. A producer’s socket now spans the task’s full lifetime, not just the initial submit.
Why a single daemon?
Multi-node queues pay a tax in the form of leader elections, replication logs and consistent hashing. Quicx is optimised for the much more common case where your queue lives on the same host (or at worst, the same availability zone) as your producers and workers. One daemon measured 74,300 tasks/sec end to end on a single thread over loopback, at a 51 µs median — see Benchmarks for the harness and the caveats. Because of PMAD it does that with zero allocation jitter and no memory growth under sustained load.
Scaling horizontally means running multiple independent Quicx daemons behind a simple TCP load balancer. A task never crosses daemons — it’s queued, run and completed on whichever instance the producer happened to land on — so there’s still no replication to coordinate. The one thing that changed in v1.0.3: since completion is now reported back on the producer’s original connection, that socket has to stay pinned to its daemon for the life of the task, not just for the initial submit.