An events page can tolerate an old description. Selling the same seat to two people is not an acceptable delay. The word “consistency” must be associated with concrete operations, data, and rules rather than a global label for an entire architecture.
The starting point is stating an invariant and identifying the component with authority to protect it. Read copies, caches, and derived views can help serve information, but availability does not automatically give them the right to decide a sale.
Properties answer different questions
| Property | Useful question |
|---|---|
| Atomicity | Which changes commit or abort together? |
| Isolation | Which concurrent operations can a transaction observe? |
| Durability | Which failures can an acknowledgement survive without losing a change? |
| Visibility and freshness | Which version can a read observe, and when? |
| Authority | Who can decide the valid state of this data? |
A local transaction can be atomic without making every external read immediately fresh. A durable replica can lag. A quickly responding cache can contain an earlier version. Separating these questions allows mechanisms to be compared without attributing guarantees they do not offer.
Isolation and local constraints
In a relational database, constraints, conditional updates, and transactions protect declared rules. If two buyers first query “available” and then write “sold,” an application precheck does not prevent the race. The authoritative decision must coordinate that transition and check affected rows or the conflict that occurred.
PostgreSQL offers isolation levels with different contracts. Read Committed can observe different snapshots between statements; Repeatable Read retains a more stable view but does not serialize every possible rule. Serializable can abort a transaction when it cannot preserve a serializable outcome; the application needs to retry the complete transaction safely.
Operation identity and business constraints still matter. A stronger level does not turn an external charge into part of the commit. It also does not replace permissions or input validation. The choice is checked against the specific invariant under concurrency and aborts.
Authority and inventory projections
In the ticketing system, Postgres retains firm holds and sales; the map and search show projections. If the map says available but the transaction rejects the hold, inventory authority prevails. The interface must communicate that outcome and update its view.
The Redis cache can coordinate provisional selections or offload reads. Its TTL does not decide on its own that a seat may be sold. The hold_id, owner, and expiry are validated against authoritative state at confirmation. An absent or expired copy does not invalidate a still-active firm hold in the database.
--- config: look: handDrawn --- flowchart TD read["Event query"] --> projection["Projection or cache<br/>Declared freshness"] write["Seat hold"] --> authority[("Authoritative inventory<br/>Constraint + atomic transition")] authority -->|"Commit"| event["Durable event"] event --> projection authority --> result["Acceptance or conflict"] projection --> view["User-facing view"]
The event can arrive late. The hold response comes from the authoritative transition rather than assuming every copy already has the same version.
Reads across replicas and regions
A read can require read-your-writes, monotonic ordering, bounded staleness, or a linearizable decision. These are different contracts. After a write, sending a client to a lagging replica can show an order as missing even when the acknowledgement was valid.
Reading from the primary or retaining a progress reference can help, depending on the engine and topology. Promoting a replica, changing routes, and losing responses introduce additional conditions to verify. Calling a database “strong” is insufficient without explaining which reads and writes receive that guarantee.
During a network partition, CAP helps discuss whether an operation can continue while preserving its read and write contract. It is not a generic choice of “two out of three” that decides everything about the product. Search can degrade while a sale requiring authority rejects or waits within bounds.
Distributed boundaries and intermediate states
2PC coordinates an atomic commit decision among prepared participants; read isolation needs additional system guarantees. Resources can remain held while a decision is resolved. It does not make arbitrary distributed operations instantaneous.
A Saga commits local steps and represents intermediate states. Compensation is a new business effect, can fail, and does not automatically erase what happened. It fits when the product admits that recovery. If an invariant requires a strict common decision, its data must be colocated under suitable authority or use the engine’s specific distributed guarantees.
An outbox derives views with recovery, without making their reads atomic with authority. Lag and reconstruction rules belong to those views’ contracts.
Declare freshness and resolve conflicts
A cache policy must decide which lag is acceptable and what happens if an old read refills an entry after invalidation. Versions and conditions can prevent an old copy from replacing a newer one. TTL alone does not guarantee an absolute age bound if the source or refill was already stale.
When concurrent writes are accepted, reconciliation belongs to the domain. Last-write-wins can lose a valid change, and machine clocks do not create a reliable global order by default. Summing counters, assigning inventory, and editing a description can need different strategies.
Verify observable rules
Tests introduce simultaneous buyers, reads from lagging replicas, and connectivity loss between regions. They also invalidate a cache during a slow read, abort serializable transactions, and leave a Saga partially completed. Verification observes the invariant rather than only the HTTP status.
The authority of each decision, versions read, conflicts, and recovery states are recorded. Observability must explain an old response without confusing it with data loss and identify a real violation even when every request returned success.
References: transaction isolation and PostgreSQL integrity constraints.