The Data Structure Hidden Inside Every Distributed Transaction
Hatched by Kai Nguyen
Aug 26, 2026
11 min read
2 views
94%
What if a failed payment is not primarily a database problem, but a data structure problem?
That question sounds strange until you notice what a distributed transaction actually contains: a sequence of actions, a set of dependencies, a history of completed steps, and a collection of relationships between services. In other words, it is data in motion. The way that data is organized determines not only how quickly a system responds, but also whether the system can recover when reality refuses to follow the happy path.
A local function can often rely on a stack, a queue, an array, or a hash table because its boundaries are clear. A distributed business process has no such luxury. Its state is scattered across services, its operations happen at different times, and its failures may arrive after some work has already succeeded. The central engineering challenge is therefore not simply to choose a fast data structure or to coordinate a transaction. It is to choose a structure for progress, uncertainty, and reversal.
The real question is not where data lives, but how reality changes
When programmers choose a data structure, they are making a prediction about the future. An array predicts that the collection will remain reasonably stable and that direct access will matter. A linked list predicts frequent insertions and deletions. A queue predicts that arrival order should determine processing order. A hash table predicts that rapid lookup by key is more important than preserving order.
Every structure encodes a theory of use.
This is easy to overlook because data structures are often taught as neutral containers. They are not neutral. They are policies disguised as mechanisms. A stack says the newest event deserves attention first. A queue says fairness means serving the oldest waiting item first. A tree says hierarchy is meaningful. A graph says relationships matter more than sequence. A hash table says identity is the shortest path to retrieval.
Distributed transactions make these hidden policies visible. Consider a commerce workflow:
- Reserve inventory.
- Charge a customer.
- Create a shipment.
- Send a confirmation.
Each operation may belong to a different service and a different database. There is no single memory location in which the entire transaction exists. The system must represent its progress through messages, recorded state, and compensating actions. If shipment creation fails after payment succeeds, the system must not merely report an error. It must decide what the previous success now means.
That is the deeper connection between data structures and distributed consistency: both are ways of organizing change under constraints.
A structure is good when its built in rules match the operations the system must perform. A distributed workflow is reliable when its representation of progress matches the ways that workflow can succeed, fail, retry, and be undone.
Performance is not just the speed of a successful operation. In a changing system, performance also includes the cost of discovering, explaining, and repairing failure.
A Saga is a data structure for reversible progress
The Saga pattern is usually introduced as a solution to a practical limitation: a business process may span several services, while traditional atomic transactions cannot safely cover all of them. Instead of pretending that the entire process can commit as one indivisible action, the system breaks it into local transactions.
Each service completes its own work. If a later step fails, earlier steps are counteracted through compensation transactions. Services communicate through messages or events, either under the direction of a central orchestrator or through a choreography in which each service reacts to what the previous service announced.
Viewed this way, a Saga is more than a transaction pattern. It is a structured history of intentions and consequences.
The workflow has a forward path:
Reserve inventory -> Charge customer -> Create shipment -> Confirm order
It also needs a reverse path:
Cancel reservation <- Refund customer <- Cancel shipment <- Retract confirmation
The reverse path is not a perfect mirror. A refund may take time. A shipment may already be in transit. An email cannot truly be unsent. Compensation is therefore not time travel. It is a new set of operations that restores an acceptable business state.
This resembles a stack, but only partially. An undo feature in a text editor commonly uses a stack because the newest edit is the first edit to reverse. A function call stack works for the same reason: the most recently entered context must finish before the caller can resume. For tightly nested operations, last in, first out is exactly right.
A distributed business process is rarely so clean. Its steps may overlap. Messages may be delayed. A retry may cause an action to appear twice. A service may complete its local transaction but fail before publishing the event that announces completion. The system therefore needs more than a stack. It needs a durable, observable, idempotent history that can answer questions such as:
- Which local actions completed?
- Which messages were sent?
- Which messages were received?
- Which compensations have already run?
- Is the workflow waiting, retrying, completed, or irreparably damaged?
This is where several familiar structures combine. A queue holds work waiting to be processed. A hash table allows fast lookup of a Saga by its unique identifier. A graph represents dependencies between services and the possible paths from success to compensation. A log or append only sequence preserves the history needed for diagnosis and replay.
The architecture is not choosing one data structure. It is composing several access patterns around one unstable fact: the system must remember what happened because no central observer can assume that everything happened together.
The hidden cost of choosing the wrong shape
The classic tradeoffs of data structures become architectural tradeoffs when the system grows.
An array offers compact storage and fast indexed access. It is ideal when the size and shape of the collection are predictable. But inserting an item near the front can require shifting many elements. In a distributed workflow, an array like mental model appears when engineers assume that every step will happen in a fixed sequence, with no interruptions or branching. That assumption makes the happy path simple, but it becomes expensive when a new approval, retry, or exception must be inserted into the process.
A linked list provides flexible insertion and deletion, but reaching a particular element requires traversal. This resembles a workflow that is easy to extend locally but difficult to inspect globally. The next step is available through a pointer, yet finding the full context may require following the chain one service at a time. Flexibility has been purchased with slower discovery and additional memory.
A queue provides order and back pressure. It is invaluable when requests should be processed in arrival order, such as payment jobs, inventory updates, or notification delivery. But a basic queue does not understand business importance. If a low value report generation task enters before an urgent fraud review, FIFO order alone cannot express the priority the business actually needs.
A hash table provides nearly constant time lookup by key, until collisions become frequent. Distributed systems have their own form of collision: two messages or retries may refer to the same business action. If the system has no idempotency key, the same payment might be charged twice. The lookup structure is fast, but the identity model is weak.
A tree is useful when authority and containment are clear. It can represent an order hierarchy, a file system, or an organization chart. But many real business workflows are not trees. A payment may affect an account, an order, a fraud model, an inventory reservation, and a shipment. Those entities share relationships that cross branches. A graph is a better representation when dependencies are many to many and when the question is not “who is this item’s parent?” but “what else is affected if this relationship changes?”
These comparisons reveal a useful design rule:
The wrong data structure does not merely make an operation slower. It makes certain failures difficult to represent.
If a workflow needs reversal but is modeled only as a forward sequence, compensation becomes an afterthought. If it needs causality but stores only the latest status, debugging becomes guesswork. If it needs to distinguish duplicate requests from new requests, a generic queue is insufficient without a key based identity layer.
Orchestration and choreography are different ways to encode control
The two common ways to implement a Saga can also be understood as competing structures for control flow.
In orchestration, a central coordinator directs each service. It knows the expected sequence, sends commands, records responses, and decides when to compensate. This resembles a tree or a controlled traversal: the orchestrator is the root, and each service is a branch that performs one part of the work.
The strength is visibility. There is one place to inspect the workflow state, define timeout behavior, and specify compensation rules. The weakness is concentration. The orchestrator can become a bottleneck, a source of excessive coupling, or a hidden single point of conceptual failure. If every new service requires edits to a central controller, the system may remain operationally distributed but structurally centralized.
In choreography, services publish events after completing their local transactions. Other services listen and respond according to their own responsibilities. This resembles a graph: no single node owns the entire path, and behavior emerges from the relationships among participants.
The strength is autonomy. Services can evolve independently, and new consumers can react to existing events without changing the publisher. The weakness is opacity. When a process fails, the path through the graph may be difficult to reconstruct. A business rule can be spread across many handlers, and a change in one subscription may alter behavior far away from the code being edited.
Neither structure is universally superior. The right choice depends on which access pattern matters most.
Choose stronger orchestration when the business process has strict sequencing, complex compensation, regulatory visibility, or a high cost of ambiguity. Choose choreography when services are genuinely independent, events have stable meaning, and the organization can invest in tracing, contracts, and operational observability.
A useful hybrid is often overlooked. Keep the services autonomous, but maintain a durable workflow record that acts as a queryable index of the distributed process. The record need not control every action. It can instead provide the missing ability to answer: where is this business operation, what has it touched, and what remains uncertain?
Design for the failure path before optimizing the success path
The most practical way to apply these ideas is to design a workflow from its failure modes backward.
Start by listing every local transaction and asking four questions:
- What does success change?
- What evidence proves that the change happened?
- Can the operation be safely retried?
- What compensating action restores an acceptable state?
The third question forces idempotency into the design. A service should be able to receive the same command more than once without producing an unintended duplicate effect. This often requires a stable operation key and a record of processed requests. That record is effectively a hash table for business actions, turning an ambiguous retry into a recognizable repetition.
The second question forces durable history. A status field containing “failed” is not enough. It cannot tell you whether payment failed before authorization, after authorization, or after the bank accepted the charge but before the response arrived. A sequence of state transitions gives the system a memory of uncertainty.
The fourth question forces realism. Some effects can be reversed exactly, such as releasing an inventory reservation. Some can be economically reversed, such as issuing a refund. Some can only be followed by a corrective action, such as sending a second message that clarifies an incorrect first message. Good compensation design distinguishes these cases instead of treating every operation as if it had a clean undo button.
Then evaluate the workflow using the same dimensions used to evaluate a data structure:
- Access: How quickly can operators find the state of one business operation?
- Mutation: How easily can a new step or exception be introduced?
- Ordering: Does arrival order determine correctness, or are priorities required?
- Memory: How much history must be retained for recovery and audit?
- Connectivity: Are dependencies linear, hierarchical, or networked?
- Failure cost: What happens when an action is repeated, delayed, or only partially completed?
This framework changes optimization priorities. A hash lookup that saves milliseconds is not the main victory if a missing compensation record causes hours of manual reconciliation. A queue that maximizes throughput may be harmful if it hides urgent work behind unimportant tasks. A highly flexible event graph may become operationally expensive if nobody can trace why a customer was charged.
Key Takeaways
- Treat data structures as policies. Before selecting an implementation, identify whether your dominant need is direct access, ordered processing, flexible mutation, hierarchy, relationship mapping, or identity based lookup.
- Model distributed workflows as histories, not just statuses. Record meaningful state transitions, emitted messages, received messages, retries, and compensation attempts.
- Give every business action a stable identity. Idempotency keys and processed action records prevent retries from becoming duplicate charges, reservations, or shipments.
- Design compensation beside the forward operation. For every local transaction, define what restoration means, how long it takes, and whether it is exact, economic, or corrective.
- Choose orchestration or choreography according to observability needs. Central control improves visibility; event based autonomy improves independence. In either case, make the workflow’s state easy to inspect.
The deepest lesson is that scalability is not merely the ability to handle more data or more requests. It is the ability to preserve meaning while more things happen at once.
A small program can get away with forgetting the shape of its state because one developer can hold the whole process in mind. A large system cannot. It needs explicit structures that encode order, identity, dependency, history, and reversal. Data structures provide these ideas at the level of memory and algorithms. Saga patterns provide them at the level of services and business operations.
The connection is more than an analogy. Every distributed architecture eventually becomes a data structure for organizational action. It decides what is remembered, what is processed first, what can be found quickly, what may be changed, and how failure is repaired.
So the next time a system design discussion asks which database, queue, or transaction pattern to use, ask a more revealing question: What shape does our uncertainty have? Once that shape is visible, the right structure is often no longer a matter of taste. It becomes a way of making the system’s promises honest.
Sources
Hatch New Ideas with Glasp AI 🐣
Glasp AI allows you to hatch new ideas based on your curated content. Let's curate and create with Glasp AI :)
Start Hatching 🐣