Modeling an Employee Org Chart in SapixDB Using Saga Transactions
When One Write Is Never Enough
Picture this: a customer clicks "Place Order" on your e-commerce site. Two things have to happen for that order to be real — the inventory count drops by one, and the customer's payment goes through. Simple enough on paper. But in a distributed system where inventory lives in one agent and payments live in another, "simple" gets complicated fast.
What happens if the inventory deducts successfully but the payment fails? You've now sold stock you didn't actually sell. You need to put that inventory back — immediately, reliably, and without a human manually cleaning up records at 2 a.m.
This is the problem SapixDB's Saga Transactions are built to solve.
What Is a Saga Transaction?
A saga is a way to write to multiple agents atomically — meaning either all of the writes succeed together, or none of them stick.
The word "atomically" might make you think of two-phase commit (2PC), the traditional distributed database approach where a central coordinator locks all participants, checks that everyone is ready, and then either commits or rolls back. That approach works, but it comes with real costs: distributed locks, a coordinator that can become a single point of failure, and latency that scales with the number of participants.
SapixDB takes a different approach. There is no central coordinator in a saga. There are no distributed locks. Instead, each step executes in sequence, and if any step fails, SapixDB handles compensation automatically — by writing a TOMBSTONE record to every previously-applied step, rolling back the changes at the data layer itself.
It's a fundamentally simpler model, and the fault tolerance is structural rather than bolted on.
The E-Commerce Order Example
Let's make this concrete. Suppose you have two agents running in your SapixDB cluster:
-
inventory-agent— owns stock levels for every product SKU -
payments-agent— records charge attempts and confirmations
A customer orders one unit of SKU WIDGET-42. Your saga needs to do two things in this exact order:
- Write a record to
inventory-agentdeducting one unit fromWIDGET-42 - Write a record to
payments-agentcharging the customer's card
Step 1: Deduct Inventory
SapixDB appends a record to the inventory agent's strand:
POST /v1/records (on inventory-agent)
{
"payload": {
"type": "inventory_deduction",
"sku": "WIDGET-42",
"quantity": -1,
"reason": "order_fulfillment",
"order_id": "ord_8812"
}
}
The agent signs this record, hashes it, and links it to the previous record in its strand. The response comes back with a record_id and a hash — cryptographic proof the write happened.
So far so good. Now step two.
Step 2: Charge Payment — And It Fails
SapixDB attempts to write to the payments agent:
POST /v1/records (on payments-agent)
{
"payload": {
"type": "payment_charge",
"order_id": "ord_8812",
"amount": 29.99,
"currency": "USD",
"card_token": "tok_visa_4242"
}
}
The payments agent comes back with an error — maybe the card was declined, maybe the payments service is temporarily unreachable. Either way, this step fails.
Automatic Compensation: The Tombstone Mechanism
Here's where SapixDB's saga behavior kicks in. Because step 2 failed, the saga cannot be considered complete. The inventory deduction from step 1 is now an orphaned write — it happened, but the order it belongs to never completed.
SapixDB resolves this by writing a TOMBSTONE record back to the inventory agent, targeting the record written in step 1:
POST /v1/records (on inventory-agent, automatic compensation)
{
"payload": {
"type": "inventory_deduction",
"sku": "WIDGET-42",
"id": "<record_id from step 1>"
},
"flags": 2
}
flags: 2 is SapixDB's tombstone flag. The record is logically removed — the inventory deduction is cancelled. The original record is still in the strand (the chain is never truncated — remember, SapixDB is append-only), but it is now marked as compensated. Any query for the current stock level of WIDGET-42 will reflect the rollback correctly.
No manual intervention. No cleanup script. No coordinator timing out. The compensation is written automatically as part of the saga protocol.
Why No Two-Phase Commit?
It's worth pausing on this design choice, because it has real practical consequences.
Two-phase commit requires every participant to hold locks during the "prepare" phase while the coordinator waits for votes. In a busy system, this means inventory records and payment records are locked simultaneously, blocking other operations. If the coordinator crashes mid-commit, you're left with participants in an uncertain state that requires operator intervention to resolve.
Sagas sidestep all of that. Each step commits immediately and independently to its own strand. If a later step fails, the earlier steps are compensated — not rolled back via locks, but corrected via new append-only records. The result is a system that is:
- Lock-free — no step holds a resource hostage while waiting for another
- Coordinator-free — SapixDB handles compensation internally, not through an external orchestrator
- Audit-complete — every step, including every compensation, is permanently recorded in the chain with a cryptographic signature
Because SapixDB is append-only by design, the full history of the saga — the original writes and the tombstones — stays in the strand forever. You can query exactly what happened and when, which matters enormously for order disputes, financial reconciliation, or compliance audits.
Designing for Saga Correctness
A few things to keep in mind when structuring sagas in SapixDB:
Order your steps by risk
Put the most likely-to-fail step last. In the e-commerce example, payment failures are more common than inventory write failures, so charging the card last means you rarely need to compensate the inventory deduction. If you reversed the order — charge first, deduct inventory second — you'd be issuing refunds every time inventory had a hiccup.
Make each step idempotent
SapixDB's append-only strand means every write is permanent. If your orchestration layer retries a step, make sure a duplicate write produces a logically harmless result. Include an order_id or a saga_id in every payload so duplicate records can be identified and filtered when querying.
Keep saga scope narrow
A saga that spans two agents is straightforward. A saga that spans eight agents with branching logic starts to look like a distributed program — and should probably be decomposed into smaller, well-bounded workflows. The fewer the steps, the simpler the compensation logic.
What Gets Written, What Gets Kept
Because SapixDB is append-only, a completed saga — successful or compensated — leaves a permanent, verifiable record:
| Step | Agent | Record Type | Outcome |
|---|---|---|---|
| 1 | inventory-agent | inventory_deduction | Written, signed, chained |
| 2 | payments-agent | payment_charge | Failed — no record written |
| Compensation | inventory-agent | TOMBSTONE (flags: 2) | Written, signed, chained |
This is not a log that can be edited. Each record is hashed with BLAKE3, signed by the owning agent's Ed25519 keypair, and linked to its predecessor. Anyone auditing the strand later can verify that the deduction happened, that the payment failed, and that the compensation was applied — without trusting any single party's account of events.
The Bigger Picture
Saga transactions reflect something fundamental about how SapixDB is architected. Because every agent owns its own strand, and every strand is an append-only chain of signed records, the database's building blocks already support compensation naturally. A tombstone is just another record — subject to the same signing, hashing, and chaining as any other write. There's nothing special to configure. The trust model doesn't change between normal writes and compensating writes.
That consistency — between how data is stored normally and how failures are handled — is what makes sagas in SapixDB predictable to reason about, even in production systems under load.
For the full saga API reference and configuration options, the SapixDB documentation covers both the protocol design and agent-level saga endpoint details.
If you're building any workflow that writes to more than one agent — order processing, multi-step onboarding, financial transfers — sagas are worth understanding before you need them. The best time to design compensation logic is before your first failure, not after.
Top comments (0)