When a messaging platform is small, every send is fast. The slowdown creeps in later, at volume, when thousands of messages move through the system at once and the parts that were fine at low scale start competing with each other. Over the past several weeks our engineering team ran a focused initiative to find and remove those bottlenecks in the path a message takes from “send” to “sent.” This post is a look under the hood at what we changed and why it makes high-volume sends faster and more predictable.
Unlike a typical “what’s new” roundup, this is one story: the send path, re-engineered end to end.
The Problem: Fast Enough, Until It Wasn’t
A single SMS or email send is trivial. The hard part is a campaign blasting tens of thousands of messages, where three kinds of contention show up at once:
- → The database doing redundant work on every message
- → Services making more network calls than they need to
- → Shared records being rewritten so often they block each other
We attacked all three.
Three HTTP Calls Became One
Before sending a message directly, the platform has to answer a few questions: is this campaign locked, is the account within its limits, is it allowed to send right now? Those checks were three separate, sequential HTTP calls between services on every single direct send. At low volume, invisible. At high volume, that is three round trips multiplied by every message, and the latency compounds.
We consolidated them into a single endpoint that performs the campaign lock check and all limit validations in one call. One network round trip instead of three, with the campaign lock returned in the same uniform shape as every other limit result so the calling service handles it cleanly. Internal errors are now distinguishable from genuine limit violations, so a transient hiccup no longer looks like a hard “you are over your limit.”
We Stopped Rewriting the Whole Campaign on Every Message
This was the big one. As a campaign sent, each message was triggering a full rewrite of the campaign record. Picture tens of thousands of messages each saving the entire campaign row back to the database, all contending for a lock on the same record. The campaigns table became the bottleneck, and sends slowed down exactly when they were busiest.
We replaced the full-row save with a targeted update that writes only the specific fields that actually change during a send. No more rewriting unchanged data, no more pile-up of writes fighting over the same row. Campaigns now move through the “sending” state faster and far more predictably at high volume.
The Database Stopped Re-Reading the Same Data on Every Request
Every authenticated request was reloading account, subscription, and settings data straight from the database, even though that information barely changes within a session. We moved that work out of the hot path:
- → Authentication data is now cached in the security token filter, eliminating per-request database loads on every single call
- → The subscription plan is cached in the security context, removing an eager database join that rode along with every request
- → Account settings no longer load eagerly; they are cached and read from fast in-memory storage
- → Country-code and subscription-plan catalog data moved into a Redis cache instead of being queried each time
We also fixed the data model itself where it was forcing extra work: a business-type field that used to pull in a related table via an eager relationship is now a plain column, and large related records that were loading eagerly in the send path were replaced with lightweight projections that fetch only the fields actually needed.
The cumulative effect is dramatically less database load per request, which is what lets the whole platform stay responsive when many users and campaigns are active at once.
Connection Pools That Do Not Cold-Start Under Load
When a burst of traffic arrives, a database connection pool that has let its connections go idle has to open new ones right at the worst moment, adding latency to the first wave of a big send. We added a minimum-idle setting to the HikariCP connection pools across the sending services (campaign sender, email campaign, email sender, SMS tracker, and the client backend’s primary and replica pools), so a baseline of warm connections is always ready. The burst gets served immediately instead of waiting on new connections.
A Real Bug: Messages Stuck in PENDING
While we were in the send path, we found and fixed a subtle correctness issue: a query that fetched a campaign’s messages without a stable sort order could leave some messages stuck in a PENDING state. Adding an explicit ordering made the processing deterministic, so messages reliably move all the way through instead of occasionally stalling.
Why This Matters for You
None of this adds a button to the UI. You feel it as sends that stay fast when they are large, states that update predictably instead of lagging, and a platform that does not slow down just because it is busy. If you run high-volume SMS or email campaigns, that reliability under load is the difference between a send that finishes on schedule and one that drags.
CloudContactAI is built by AllCode, an AWS Advanced Tier Services Partner. This kind of send-path profiling, caching, and contention work is the same AWS-native performance engineering discipline AllCode applies across its platforms.
Have feedback on any of these changes, or a feature you would like to see next? Reach out to your account team. We would love to hear from you.
