Skip to main content
Technical Systems

Modernising Systems Where Eventually Is Not Acceptable

Protect critical invariants at the point of commitment while allowing delay where the business can tolerate it.

Choose consistency boundaries around business invariants. Understand atomic reservations, linearizability, transaction isolation, asynchronous projections, and recovery.

Modernising Systems Where Eventually Is Not Acceptable

Two customers attempt to reserve the last available item. Both application servers read a stock count of one, and both proceed as though that observation authorises a sale.

The problem is not that every part of the system needs instant agreement. The problem is that the operation committing inventory failed to preserve a specific rule: confirmed reservations must not exceed the available quantity.

Eventual consistency allows replicas or derived views to converge after updates, assuming the conditions needed for propagation hold. It does not inherently lose money or make transactional applications impossible. It also does not establish a maximum convergence time by itself.

Modernisation requires deciding where temporary disagreement is acceptable and where a business commitment needs stronger coordination.

Start With the Invariant

An invariant is a condition that must remain true across the operations the system permits. For the reservation service, it might be that each unit can belong to at most one active reservation.

That is more useful than a requirement to make the whole system strongly consistent. It identifies the state and operations that must be coordinated.

OperationRequired property
Browse a product pageA slightly stale availability indication may be acceptable
Confirm a reservationCapacity must be allocated without overselling
Send confirmation emailDelivery can follow the committed reservation
Update an analytics dashboardA delayed projection may be acceptable

The user interface should reflect those boundaries. Displaying availability is not a promise that a later reservation will succeed. Confirmation should follow successful allocation.

A Fresh Read Does Not Make a Read-Then-Write Safe

Even if both servers read the latest value, they can still race between reading and writing. Correctness needs to cover the state transition.

Possible mechanisms include a conditional update, an appropriate database transaction, a uniqueness constraint, or serialising the relevant operations through an authoritative owner. The choice depends on the data model and failure requirements.

For example, the authoritative service can atomically decrement available capacity only if sufficient capacity remains, then record the reservation in the same transaction. It must inspect the operation’s result before confirming success.

Linearizability describes operations behaving as though they take effect at a single point between invocation and response, respecting real-time ordering. Jepsen’s explanation is useful for understanding that guarantee. It does not mean that an arbitrary sequence of separate calls becomes one atomic operation.

Transaction isolation is another dimension. The selected isolation level and constraints must protect the invariant across all records involved; the word “transaction” alone is insufficient.

Keep the Commitment Boundary Small

The reservation does not need to wait for every downstream activity before becoming authoritative. Once the reservation commits, email, analytics, and search views can be updated through separate processing.

Commit the reservation before downstream processing
Commit the reservation before downstream processing Reservation request Authoritative validation and atomic allocation Committed reservation Customer confirmation Notification processing Reporting projection
The authoritative reservation is committed first. Confirmation, notifications, and reporting follow that boundary.

A smaller coordinated boundary can reduce contention and simplify recovery. It also makes authority clear: a delayed dashboard does not undo the reservation.

When state changes and event publication must remain connected, a transactional outbox can record the event alongside the business update in the same database transaction. A separate process publishes it. Delivery may be repeated, so consumers still need appropriate duplicate handling.

The outbox addresses a publication gap. It does not make all downstream processing one distributed transaction.

Make Pending States Honest

Some workflows span systems that cannot commit together. A purchase might involve inventory, payment authorisation, and a delivery service.

Represent the intermediate state explicitly. A request can be pending while the required actions complete, and the customer can be informed of the result once the workflow knows enough to make that promise.

Compensating actions may release a reservation or reverse an authorisation when later work fails. They are suitable only when the business accepts the intermediate states and the available recovery behaviour.

If the invariant cannot tolerate those states, the architecture needs a different boundary or protocol. Calling a workflow a saga does not make every temporary inconsistency acceptable.

Unavailability Can Be the Correct Outcome

When the system cannot contact the authority that allocates the last item, it needs a defined response. It might reject the request temporarily or accept a clearly labelled pending request for later assessment.

Confirming success from an uncoordinated stale copy would weaken the invariant. That trade-off should be explicit rather than hidden behind a general availability target.

There are other designs, such as allocating bounded quotas to independent regions. Those can preserve a global capacity limit if the allocation protocol is correct, but they may leave capacity unused in one region while another rejects requests.

The business must decide which constraints matter. Coordination, latency, availability, and utilisation interact; a product requirement cannot remove those interactions by asking for everything simultaneously.

Migration Must Preserve a Single Allocation Rule

During modernisation, old and new services may both be capable of accepting reservations. That is dangerous if each believes it controls the full inventory.

Define ownership during the transition. Requests might route through one authority while other components are replaced, or ownership might move by a controlled partitioning scheme.

Comparison traffic should not allocate real stock twice. Reconcile migration state and examine concurrent requests, duplicate delivery, uncertain outcomes, and failover before moving authority.

The surrounding runtime modernisation can change substantially while the critical commitment remains bounded and testable.

Consistency Is a Property of the Decision Path

The useful goal is to preserve the rules required at commitment and make delayed information identifiable everywhere else. That allows many systems to combine coordinated writes with asynchronous views.

Define the invariant, select mechanisms that enforce it, and specify what happens when those mechanisms are unavailable. Then evaluate the complete path, including retries and recovery.

A system is dependable when its confirmations mean what users and downstream services believe they mean.

Key Takeaways

  • Protect specific business invariants rather than demanding one consistency model everywhere.
  • Fresh reads do not make separate read-and-write operations atomic.
  • Keep authoritative commitments distinct from asynchronous notifications and projections.
  • Make uncertainty, pending states, and availability trade-offs explicit during migration.