dispenserd — the HTTP-poll queue

Every long-lived web platform ends up with a queue. Currnt’s queue is unusual: it’s neither Kafka nor RabbitMQ nor Redis pub/sub nor SQS. It’s a homegrown HTTP-poll system built in-house, named dispenserd. Producers POST jobs to its HTTP endpoint; workers poll the same endpoint to pull work. Acknowledgments and idempotency live in MySQL rows.

It’s not the queue you’d design today. It’s also the queue Currnt has had for nine years, and it has been more reliable than any of its replacements would’ve been at the same headcount.

The architecture

                  ┌──────────────────────────────────────────┐
                  │              dispenserd                  │
   producers ────▶│  HTTP POST /enqueue                       │
   (Sails app)    │   ├── 5 lanes (priority classes)         │
                  │   ├── in-memory queue per lane           │
                  │   └── MySQL ack rows for at-least-once   │
                  └────────────────┬─────────────────────────┘

                          (workers poll over HTTP)

       ┌───────────────────────────┼───────────────────────────┐
       ▼                           ▼                           ▼
   worker A                    worker B                    worker N
   Supervisord                 Supervisord                 Supervisord
   ↓                           ↓                           ↓
   4,591-line entry            4,591-line entry            4,591-line entry
   ↓ dispatches by job type    ↓                           ↓
   19 handler scripts          (same set)                  (same set)

The five lanes

Each lane corresponds to a class of work with its own latency tolerance and resource profile. Producers specify the lane on enqueue.

LaneTypical jobsLatency targetWorkers
send_emailTransactional and outreach emailsSecondsMany
send_outreachBatch outreach sendsMinutesMany
import_prospectsCSV imports, dedup runsTens of minutesFew
enrichHunter.io and LinkedIn-scraper jobsMinutesFew
notifyPush notifications, slack-like internal pingsSecondsOne or two

The exact mapping of workers to lanes lives in Supervisord config; some workers are dedicated to a single lane, some round-robin across lanes.

The 19 handlers

Each enqueued job carries a type field. The worker entry script dispatches to one of 19 handler functions based on this type. A condensed list, grouped by purpose:

A nineteenth handler is a generic noop used for testing.

The handler dispatch lives at the top of a 4,591-line worker entry file — by far the largest single file in the codebase. Reading it is roughly the way to understand the runtime: the queue is small, the entry is big, the handlers are inline.

Retry semantics

Operational quirks

Why this page matters for the rest

The queue is the spine that connects everything: when Domain B / Treehouse sends an outreach blast, when Domain F sends an invoice, when Domain D publishes a post, when the AI co-facilitator runs — they all go through here.