Infrastructure

Queued Sending: Why It Matters at Scale

ByLalit Kumar Jangid
6 minSeptember 26, 2026
Queued Sending: Why It Matters at Scale

Sending email looks trivial until volume arrives. A script that works perfectly at a hundred messages per hour will drop a meaningful fraction of a fifty-thousand-message campaign, and it will do so quietly. Understanding why requires looking at what happens between "send" and "delivered", which is where most of the engineering actually lives.

The naive approach and why it fails

The direct approach opens a connection to the recipient's mail server and hands over the message. This is fast when volume is low. It breaks in three specific ways as volume rises.

First, mailbox providers throttle. Receiving servers limit how many connections and messages they will accept from a given sender per unit time, and the limits vary by provider and by your reputation with them. Exceed it and you are temporarily rejected.

Second, a synchronous loop has no memory. If the process dies at message 30,000 of 50,000, the remaining 20,000 are simply lost. There is no record of what succeeded unless you built one.

Third, one large send starves everything else. A password reset triggered while the marketing campaign is mid-loop competes for the same connections and can be delayed by minutes, which is visible to the user.

What a queue changes

A queued architecture inserts a durable buffer between "this message should be sent" and "this message is handed to a mail server". The campaign is decomposed into individual jobs, each written down before any delivery is attempted. Delivery workers then consume jobs at a controlled rate.

That single change alters the failure modes:

  • Throttling becomes pacing. A temporary rejection is not a lost message; the job is retried with backoff. Your effective send rate adapts to what the receiving server will accept rather than being fixed by your loop speed.
  • Crashes become resumable. Jobs live in the queue independently of the worker process. A worker restarting picks up where it left off.
  • Concurrency becomes controllable. You can cap parallel connections globally, or per receiving domain, which is what actually protects reputation. Sending fifty thousand messages while opening two hundred simultaneous connections to one provider is how you get blocked.
  • Priority becomes possible. Transactional jobs can be processed ahead of bulk campaign jobs, so a password reset is not queued behind 49,999 marketing messages.

Retries, and the difference between temporary and permanent

Retry logic is where naive implementations quietly damage reputation. A 4xx response from a mail server usually means "come back later" and should be retried with increasing delay. A 5xx response means "do not come back" and must not be retried at all.

Retrying a 5xx is actively harmful. It means repeatedly attempting delivery to an address the server has told you does not exist, which is a strong signal of poor list hygiene. The correct handling is to record the failure, suppress the address, and never attempt it again.

Backoff should also be bounded. A message that has failed repeatedly over many hours is unlikely to succeed on the next attempt, and holding it indefinitely consumes capacity and delays the point at which you learn the address is dead.

What this means for the sender

From outside, a queued system looks slower to start and faster to finish. The first message to be handed over is not immediate, because jobs are written down first. But the campaign completes in less wall-clock time than a synchronous loop at scale, because delivery proceeds under controlled parallelism rather than one connection at a time, and because temporary rejections no longer consume a retry slot that could have gone to a message that would be accepted.

The more useful change is observability. Because every message is an individual job with a recorded outcome, you can answer questions that a synchronous sender cannot: how many are queued, how many delivered, how many are awaiting retry, how many failed permanently. Those numbers are the difference between knowing a campaign is progressing and hoping it is.

Concurrency limits and reputation

The subtlest part of queue design is deciding how many connections to open at once, and against which destinations. Provider limits are usually expressed per-connection and per-IP rather than globally, so a worker pool that opens many parallel connections to a single provider can trigger throttling even when total throughput looks modest.

The correct unit of control is therefore per receiving domain, not global. Twenty simultaneous connections spread across twenty different providers is unremarkable. Twenty simultaneous connections to one provider is a pattern that looks like abuse.

Sustained throughput also matters more than peak throughput. A sender that delivers steadily is easier for a receiving server to accommodate than one that delivers in bursts, even when the daily totals are identical. Queues make steady pacing straightforward: it is a rate setting rather than something you have to engineer into the sending code.

Observability is the real deliverable

Most teams adoptqueuing for reliability and then find the observability is what changes their operations. A synchronous sender reports an aggregate: the campaign ran, some messages failed. A queued sender can report current queue depth, delivery rate per provider, retry backlog, and permanent failure count, each broken down by domain.

That granularity turns a class of incident into a routine observation. A rising retry backlog for one provider is visible while the campaign is still running, rather than being reconstructed afterwards from bounce reports.

How Cresca implements this

Cresca processes campaigns through a BullMQ queue backed by Redis. Campaigns enter the queue as individual jobs, delivery workers process them in parallel, and concurrency is managed centrally so a large marketing send does not starve transactional traffic. Failed sends are retried with backoff, and permanent failures are recorded so they stop consuming retry capacity.

Events — delivery, open, click, bounce, unsubscribe — are emitted into the same stream that drives automation and analytics. That consistency is what prevents the common failure where an automation keeps messaging a contact who unsubscribed through a different path, since suppression state is shared rather than maintained separately by each subsystem.

Pricing

Queued delivery is part of the platform on every tier:

PlanPriceContactsEmails / month
Free$05050
Professional$29/mo5,0005,000
Premium$49/mo25,00025,000
Ultra$99/mo55,00055,000

The short version

Synchronous sending breaks at volume in three ways: throttling, unrecoverable partial failure, and starvation of urgent mail. A queue fixes all three by making each message a durable, individually-tracked job. The critical details are distinguishing retryable from permanent failures, bounding backoff, and controlling concurrency per receiving domain.

Get those right and volume becomes a throughput question rather than a reliability one.

Queued Sending: Why It Matters at Scale | Cresca Blog