Urgent.News

What's breaking now, across thousands of outlets.

Editions

Tech

Buying a phone number is a distributed transaction

The API makes it look trivial. const number = await carrier . numbers . buy ({ phone_number : " +1... " }); await db . insert ( " rented_numbers " , { user_id , e164 : number . phone_number }); await stripe . subscriptions . create ({ customer , price }); Three lines, one number, done. Ship it. What you actually wrote is a distributed transaction across three systems. They share no transaction…

Acquiring a phone number is a complex operation involving multiple systems, each maintaining separate knowledge about the same number. The code snippet provided shows a simplified three-step process: purchasing the number from a carrier, recording the number in a database, and creating a subscription with Stripe. However, this sequence represents a distributed transaction with no shared transaction log, two-phase commit, or rollback capability among the systems.

Each system independently charges or refunds for the number based on its own state, leading to potential inconsistencies.

The author, who operates a virtual phone number product, identifies several failure modes that result in financial losses. The most severe is an "orphan" number - one that exists in the carrier's inventory but is not recorded in the company's database or used by any customer. This situation occurs when the timing of events (such as a network timeout) causes the systems to diverge, leaving the number unclaimed and incurring ongoing charges from the carrier.

Other classes of failures include incorrectly billing customers for unowned numbers, continuing to charge for numbers that have been cancelled, and providing services for free indefinitely.

To mitigate these issues, the author suggests implementing a scheduled reconciliation process. This involves periodically checking the state of the number across all systems and correcting any inconsistencies. The reconciliation function should be read-only, comparing the database, carrier, and subscription records, and outputting a delta.

Based on this delta, the appropriate action can be taken, such as deleting the subscription or logging an anomaly for manual review. By separating the diagnosis from the action, the system can be tested without causing changes, reducing the risk of automated fixes harming the user experience.

Key takeaways from this story include:

- Distributed transactions across independent systems can lead to inconsistent states without a shared transaction log.

- Orphan numbers represent a significant financial risk, as they generate ongoing charges from carriers even if no customer is using them.

- A scheduled reconciliation process with clear, readable diagnosis functions is essential to maintain data consistency and prevent silent financial losses.

- The implementation should handle various edge cases, such as different carrier rules, provisioning restrictions, and regulatory requirements, to ensure a robust and user-friendly system.

Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.

Read the original at dev.to →

More in Tech

More from Thursday 20 August →