The pipeline that broke without breaking
Kiran had spent four months writing the verification pipeline at his company. Not configuring a SaaS tool. Not stitching together third-party APIs. Actually writing it — a custom pipeline designed around the specific way his business processed contact data, integrated with their CRM, their enrichment layer, and their outbound sequencing tool.
At ten thousand verifications per day, the pipeline ran exactly as intended. Jobs completed in under ninety seconds. Error rates stayed under one percent. His team relied on it the way you rely on something that has never given you a reason to question it.
Then his company closed a partnership that tripled the volume of contacts coming in overnight. Thirty thousand verifications per day, starting Monday.
The pipeline did not throw an error. It did not crash. It accepted every job, put every job in the queue, and processed every job in the order it received it. Nothing was wrong in any way the system knew how to report.
He reached the Proxy25 line through a mutual contact who told him that the team there had seen the inside of more verification pipelines than anyone else they knew, and that when a pipeline problem did not make obvious sense, that was the kind of call worth making.
That was accurate. What I found across two days of working through Kiran's codebase and logs was not a single broken thing. It was four architectural constraints, each one sitting behind the performance of the previous, each one only becoming fully visible as the one in front of it was addressed.
Why email infrastructure scalability failures look like slowness, not failure
The reason Kiran's team spent a week trying to diagnose the problem before calling anyone was that the problem produced no error signal. Every metric that reports failure was nominal. The pipeline was succeeding at processing jobs. It was just succeeding slowly enough that the practical effect was indistinguishable from failure.
A web application approaching a performance ceiling typically signals it — response times rise, error rates climb, the dashboard shows something to act on. Email pipelines operate differently because of two structural properties.
Kiran's pipeline had four layers of constraint behind each other. Each one had been masked by the one in front of it. Ten thousand daily verifications put the first constraint under just enough load to pace all the rest. Thirty thousand revealed the first constraint clearly — and then, sequentially, every other.
The write that waited for itself
The first constraint wasn't in the SMTP layer or the proxy layer. It was in the database. Every time a verification job completed, the result was written synchronously before the next job was picked up — clean, predictable, auditable, and completely sensible at ten thousand daily verifications.
At 10,000 verifications per day with each write taking roughly 11ms, the total cost across a full day was negligible. At 30,000 per day, the same pattern pushed the database past its provisioned capacity. At peak hours, individual write latency climbed to 120ms — serialized, one write at a time, before the next job could start.
The fix was batching: hold results in an in-memory buffer, flush in batches of five hundred every thirty seconds. The database went from thirty thousand individual transactions to sixty batches an hour. Total pipeline time spent on writes dropped by 92%.
This is the class of constraint that's architecturally invisible at low volume. Eleven milliseconds feels free. Applied serially at scale, it becomes the dominant cost — not because the code changed, but because the cost structure of the same code changed underneath it.
The worker count that was never meant for this load
After the write fix, the queue cleared faster — for about six hours. Then Kiran's team hit the second layer: a fixed worker pool sized at twenty, which had provided comfortable headroom at ten thousand verifications a day.
Between jobs, each worker deserializes from the queue, establishes network state, resolves the destination, manages the SMTP connection object, and writes to the result buffer — real time that never shows up in per-job processing figures. Beyond forty workers on a single machine, adding more began reducing per-worker throughput as OS scheduling and socket buffer overhead ate the gains.
The fix was distribution, not concentration: three servers running fourteen workers each, rather than one server running forty. Each worker carries an outbound connection, a DNS resolution, and an active SMTP session — per-worker costs the network stack and OS handle separately, with a capacity limit independent of the application-level worker count.
The DNS time nobody was measuring
After the worker fix, the team projected the 280,000-job backlog would clear in thirteen hours. It took twenty-two. Nine hours, unaccounted for. No error, no degraded output — just a gap between what the throughput numbers predicted and what the clock showed.
The gap was DNS resolution. Kiran's pipeline made a fresh MX record query for every single job — no caching. A batch of four hundred addresses at the same company domain made four hundred identical queries. Under the load of forty-two concurrent workers, resolution times climbed from 60–80ms to 140–180ms at peak.
The fix was MX record caching with TTL-aware expiry. On a real enterprise contact list where many contacts share company domains, cache hit rate ran above 65%. The next comparable backlog cleared in fourteen hours instead of twenty-two.
The queue that treated everything as equally urgent
Three layers cleared. Then one question exposed the fourth: if a salesperson needed a single contact verified before a call in twenty minutes, how long would they wait? The honest answer was nobody knew — it depended entirely on what else was already in the single first-in-first-out queue.
A batch of forty thousand addresses submitted at 9am sat ahead of an urgent single verification submitted at 9:15 for a 9:30 call. At ten thousand daily verifications this ordering rarely mattered. At thirty thousand, with backlog events occurring, the difference between "needs to finish in minutes" and "needs to finish by end of day" was invisible to the queue.
The fix was queue separation: an urgent lane for single-address and small CRM-triggered verifications, and a standard lane for bulk batches. Workers service the urgent lane first, and fall back to the standard lane only when it's empty. Total capacity is unchanged — only the ordering of how it's applied changes.
After separation, a single verification completed in under ninety seconds regardless of what sat in the standard lane. Bulk batches completed in four to six hours — well within what the data team actually needed. Treating all jobs identically in a queue is a design decision, not a neutral default.
The episode that revealed what was still missing
Two weeks into the rebuild, a large enterprise domain changed its mail server configuration so that every SMTP connection attempt stalled at the TCP stage for twenty-eight seconds before timing out. Not rejected — stalled. With workers distributed across three servers, the stalls occupied enough of them that two servers' effective output dropped to near zero for ninety minutes.
The pipeline had no mechanism to notice one destination was failing at an abnormal rate and stop sending it workers. The fix was a circuit breaker: track failure rate per destination domain in a rolling window; when more than 25% of attempts to a domain fail or time out within five minutes, open the circuit and route new jobs for that domain to a deferred queue. Probe every five minutes; close the circuit the moment a probe succeeds.
A circuit breaker doesn't speed up healthy processing. It stops one misbehaving destination from consuming capacity that belongs to everyone else in the queue.
The missing feedback from pipeline to pipeline
Across all five issues, one pattern repeated: the pipeline had been built to process jobs, not to communicate its own state back to the systems submitting them. When any layer was constrained, the CRM integration and batch scheduler kept submitting at their normal rate regardless — a backpressure gap.
The fix was a lightweight queue-state endpoint the CRM integration and batch scheduler check before submitting. Past 150 jobs in the urgent lane, single submissions delay fifteen seconds before retrying. Past 4,000 jobs in the standard lane, new batches hold until depth drops below 2,500. The CRM doesn't need to understand why the pipeline is busy — only the signal to pause and the signal to resume.
What the numbers looked like when the rebuild was done
All six changes — write batching, distributed workers, MX caching, queue separation, circuit breaker, backpressure — went in across three weeks, in the order the constraints were discovered. Order mattered: distributing workers before fixing the write serialization would have spread the bottleneck across three servers instead of removing it; MX caching had to come after worker distribution, because the DNS constraint was masked until the worker constraint was cleared.
None of the six changes required exotic techniques — write batching, horizontal worker distribution, DNS caching, queue separation, and circuit breakers are all standard, well-documented patterns. What wasn't standard was having all six in the right sequence, in a pipeline where the constraints interact in the specific ways they do when email verification is the workload.
Where this connects to the Proxy25 layer
Proxy25 sits at the SMTP infrastructure layer — the proxy the pipeline connects through when making verification queries. Kiran's bottlenecks were all application and architecture layer: writes, workers, DNS, queue logic. Distinct engineering problems from the infrastructure the pipeline connects to.
The connection worth making explicit: the circuit breaker Kiran added to detect misbehaving destination domains is structurally the same approach Proxy25 uses at the infrastructure layer to manage the relationship between verification IPs and receiving mail servers. Track the quality of what each destination is producing. Stop trusting destinations producing poor output. Resume when they recover. That pattern works at the queue level, and it works at the proxy level.
Built for the volume your pipeline is finally running at
Proxy25 provides the proxy infrastructure layer for verification pipelines — IP addresses with years of clean SMTP connection history with major mail servers.
Start with 500 free credits →