Build a Tiny CPaaS · Chapter 1 · 14 min
Message router core
Accept, persist, route: the outbox pattern at the heart of every CPaaS.
The Outbox pattern
The foundation of a reliable CPaaS is the separation of message acceptance from message delivery. When a request hits the ub3r API, we don't try to send the message immediately; instead, we record the intent in a database outbox. This ensures that even if the downstream carrier or the router itself crashes, the message is never lost. Once the transaction is committed, the API returns a 202 Accepted status to the client, fulfilling its initial contract.
A background worker then polls the outbox for new messages and begins the complex task of routing and delivery. This asynchronous architecture allows the API tier to remain fast and responsive even during high-traffic spikes. By decoupling these two phases, we can scale the API and the routing engine independently based on demand. This resilience is what allows the ub3r platform to maintain high availability under heavy load.
Routing logic and priorities
Routing is the brain of the CPaaS, deciding which carrier and path a message should take to reach its destination. The router evaluates several factors including destination prefix, tenant priority, and current carrier health. We use a longest-prefix match algorithm to find the most specific route for any given phone number. This allows us to optimize for both cost and deliverability by selecting the best regional partners for every request.
Rules are stored as data in a configuration table, allowing our operations team to shift traffic in real-time without a code deploy. If a specific carrier starts experiencing elevated latency in a particular country, we can instantly lower its priority. This dynamic routing capability is essential for navigating the complex and often unstable global telecommunications landscape. It ensures that the ub3r platform remains reliable even when individual carriers fail.
-- A simplified routing table query
SELECT carrier_id, priority
FROM routes
WHERE prefix = substring($1 from 1 for length(prefix))
ORDER BY length(prefix) DESC, priority ASC
LIMIT 1;State persistence
Every transition in a message's lifecycle must be recorded with a timestamp and metadata for auditing and debugging. From queued to submitted to delivered, these state changes provide a clear trail of what happened and when. We store these transitions in a dedicated history table to keep the main messages table lean and performant. This allows us to provide detailed delivery reports to our customers while maintaining high system throughput.
We also persist the raw responses from carrier APIs to help our support team investigate delivery issues. Sometimes a carrier returns a success code but the message is never actually delivered, and having the original logs is vital. This level of detail allows us to hold our partners accountable and provide accurate information to our users. A CPaaS that doesn't value its data history is a black box that is impossible to trust.
Discussion (4)
The callout about alerting on queue age instead of depth changed how we page people. Thank you.
Same experience here. Per-route metrics were the only way we found it.
The distinction between 'sent' and 'delivered' finally clicked for me here. We had dashboards treating them as the same thing for a year.
Would love a follow-up on how this behaves under carrier throttling. We see very different numbers per route.