When you think about inventory management, the first thing that probably comes to mind is a simple decrement. You sell an item, you reduce the count. But the reality of high-volume systems is far more complex, and often, the most surprising thing is how much work goes into not changing the inventory count, at least not yet.

Let’s imagine a scenario where a customer places an order for 10 units of "Widget A". This isn’t just a direct decrement. Instead, the system goes through a multi-stage process.

First, there’s the Reserve phase. The system identifies that 10 units of "Widget A" are needed. It then looks at the available inventory. If there are, say, 100 units on hand, it doesn’t immediately subtract 10. Instead, it marks those 10 units as "reserved" for this specific order. This is crucial because if another order comes in for "Widget A" simultaneously, it won’t see those 10 units as available. The inventory count on hand might still read 100, but the available quantity for new orders has effectively dropped to 90.

Here’s a simplified view of how that reservation might look in a database. Imagine an inventory_items table:

-- Before reservation
SELECT * FROM inventory_items WHERE sku = 'WIDGET-A';
-- id: 1, sku: 'WIDGET-A', quantity_on_hand: 100, quantity_reserved: 0

-- After reservation for Order #12345
UPDATE inventory_items
SET quantity_reserved = quantity_reserved + 10
WHERE sku = 'WIDGET-A' AND quantity_on_hand >= quantity_reserved + 10;

-- After update
SELECT * FROM inventory_items WHERE sku = 'WIDGET-A';
-- id: 1, sku: 'WIDGET-A', quantity_on_hand: 100, quantity_reserved: 10

The quantity_on_hand remains 100, but quantity_reserved is now 10. The available quantity for new orders is implicitly quantity_on_hand - quantity_reserved, which is 90.

Next comes the Commit phase. This is where the inventory count actually changes. If the order is confirmed, payment is processed, and the item is ready to be shipped, the system will commit those reserved units. This involves two actions: decreasing quantity_reserved by 10 and decreasing quantity_on_hand by 10.

-- During commit for Order #12345
START TRANSACTION;

UPDATE inventory_items
SET quantity_reserved = quantity_reserved - 10
WHERE sku = 'WIDGET-A' AND quantity_reserved >= 10;

UPDATE inventory_items
SET quantity_on_hand = quantity_on_hand - 10
WHERE sku = 'WIDGET-A' AND quantity_on_hand >= 10;

COMMIT;

-- After commit
SELECT * FROM inventory_items WHERE sku = 'WIDGET-A';
-- id: 1, sku: 'WIDGET-A', quantity_on_hand: 90, quantity_reserved: 0

Now, 10 units have been permanently removed from stock.

The Rollback phase is the safety net. What happens if the order fails after reservation but before commitment? Perhaps the payment fails, or the customer cancels the order. In this case, the reserved units need to be released back into available stock. This means simply decrementing quantity_reserved by the reserved amount. The quantity_on_hand remains unchanged.

-- During rollback for Order #12345
START TRANSACTION;

UPDATE inventory_items
SET quantity_reserved = quantity_reserved - 10
WHERE sku = 'WIDGET-A' AND quantity_reserved >= 10;

-- No change to quantity_on_hand

COMMIT;

-- After rollback
SELECT * FROM inventory_items WHERE sku = 'WIDGET-A';
-- id: 1, sku: 'WIDGET-A', quantity_on_hand: 100, quantity_reserved: 0

The system effectively undoes the reservation, making those 10 units available again.

This multi-stage approach is critical for handling concurrency and ensuring data integrity in distributed systems. It prevents overselling by temporarily earmarking inventory, allows for checks and balances between reservation and final fulfillment, and provides a clean way to reverse actions when necessary. Without this, race conditions would lead to incorrect inventory counts, overselling, and frustrated customers.

The specific levers you control are primarily the quantity_on_hand and quantity_reserved fields. The availability for new orders is an implicit calculation derived from these. You can also introduce additional states, like quantity_in_transit or quantity_damaged, to further refine the model.

What most people don’t realize is that the quantity_reserved field isn’t just a number; it’s a promise. When you update quantity_reserved, you’re creating a contractual obligation within your system. If that contract is broken (e.g., by a system failure before rollback or commit), you can end up with phantom inventory that’s reserved but can never be fulfilled or released.

The next logical step in this saga is understanding how to handle partial fulfillments and backorders within this reserve-commit-rollback framework.

Want structured learning?

Take the full Saga-pattern course →