Skip to content
ub3r.io

Build a Tiny CPaaS · Chapter 2 · 15 min

Carrier abstraction layer

One connector interface, per-carrier queues and throughput shaping with mx.queue.

Unified carrier interfaces

The world of telecommunications is fragmented, with different carriers using a variety of protocols from SMPP to modern REST APIs. The ub3r abstraction layer hides this complexity behind a single, unified interface that our routing engine can consume. This allows us to add new carriers or switch providers without changing the core business logic of the platform. Each connector is responsible for translating our internal message format into the specific dialect required by the partner.

Normalization of error codes and status reports is a critical part of this abstraction process. A "Failed" status from one carrier might mean something completely different from another, so we map them to a common set of ub3r statuses. This consistency allows our customers to build their own logic around delivery reports without needing to know which carrier handled the message. By providing a clean interface, we make a messy industry feel manageable.

Queue-based isolation

To prevent a slow carrier from impacting the rest of the system, we maintain separate queues for every connector. If Carrier A starts responding slowly, its dedicated queue will grow, but Carrier B will continue to process messages at full speed. This "bulkheading" strategy is essential for maintaining fair throughput across all our partners. The ub3r platform uses a sophisticated scheduler to drain these queues based on their respective rate limits and priorities.

Playground· Per-carrier queue with mx.queue
output

Waiting for output…

We use a token bucket algorithm to shape the traffic flowing into each carrier, ensuring we never exceed their agreed-upon capacity. This proactive rate limiting prevents carriers from blocking our IP addresses due to accidental flooding. By managing these limits at the edge of our system, we provide a smoother experience for our users and a more predictable load for our partners. Queue depth and processing latency are monitored continuously to detect any bottlenecks in real-time.

ts
// A simplified token bucket for rate limiting
class RateLimiter {
  private tokens = 10;
  async consume() {
    while (this.tokens <= 0) await sleep(100);
    this.tokens--;
  }
}

Health monitoring and failover

Continuous health checks are performed on every connector to ensure they are ready to accept traffic. If a connector fails several consecutive attempts or its error rate crosses a predefined threshold, the router marks it as unhealthy. Traffic is then automatically rerouted to the next best available carrier to ensure uninterrupted service. This failover mechanism is completely transparent to the end-user, who only sees that their message was delivered successfully.

Once a connector is marked unhealthy, we enter a "probing" phase where we send a small number of test messages to see if it has recovered. If the tests succeed, the connector is gradually reintroduced into the active routing pool. This controlled recovery prevents a sudden surge of traffic from overwhelming a carrier that has just come back online. This level of automation is what allows the ub3r network to self-heal in the face of infrastructure challenges.

Discussion (4)

ML
Marcus Lindqvist · 2026-10-05

Would love a follow-up on how this behaves under carrier throttling. We see very different numbers per route.

LF
Lena Fischer · 2026-10-12

Same experience here. Per-route metrics were the only way we found it.

PR
Priya Raman · 2026-10-06

Ran the playground with three concurrent sends — receipts came back out of order, exactly as the text says. Great way to make it stick.

TH
Tomás Herrera · 2026-10-07

We learnt the idempotency lesson the expensive way: duplicate OTPs during an incident. This chapter is now required reading on my team.