Course contents
On this page
Purchase rules, retries, and correctness
Deeper reading after the guided lesson: purchase rules, retries, and correctness.
Start with the guided lesson. These sections develop the mechanisms and tradeoffs beyond that first example.
Learning outcomes
- Translate product behavior into invariants, access patterns, skew, and measurable success boundaries.
- Distinguish independent transactions from competing operations on a shared ownership invariant.
- Explain why active connections, useful transactions per second, and delivered updates are separate capacity measures.
Define correctness before optimizing the path
An invariant is a property that must remain true across accepted state transitions. For the marketplace, available stock must never become negative, and a unique item must not have two owners. For a transfer, total money across the affected accounts is conserved unless the operation explicitly models a fee or issuance. Invariants turn an informal feature into an obligation that a transaction, application, and failure-recovery procedure can preserve.
A useful specification includes the state before an operation, its permitted input, its preconditions, the changes it makes, and its externally visible outcome. It must also describe uncertain outcomes. If the server commits a purchase but the response is lost, the buyer cannot safely infer that the purchase failed. The operation needs an identity that survives retries. Lesson 5 develops that protocol boundary and Lesson 37 extends it to external side effects.
The following SQL illustrates a guarded stock change. The syntax is ordinary transactional SQL; the interpretation of affected-row counts and application error handling belongs to the client implementation.
BEGIN;
UPDATE inventory
SET remaining = remaining - 1
WHERE product_id = 73 AND remaining > 0;
-- The application continues only if exactly one row changed.
-- It inserts the order in this same transaction.
COMMIT;This fragment deliberately stops short of a complete purchase implementation. It shows that the eligibility check and decrement belong in one guarded change. A real design also defines a unique operation identifier, order insertion, permissions, and rollback behavior. Reading remaining in one request and writing a decremented value in a later unrelated request does not express the same contract. Two callers may both act on an old observation.
The consistency letter in ACID refers to preserving constraints and invariants, not a promise that the database understands the business. A database can enforce a nonnegative stock constraint while the application accidentally gives the item to the wrong buyer. Correctness depends on both enforced database rules and valid application transitions.
Correctness changes the available optimizations
An approximate dashboard counter can tolerate temporary disagreement. A unique item owner usually cannot. If readers can accept a stale value, replicas and caches gain flexibility. If every purchase must respect the latest stock decision, the design needs an authoritative decision path. A change in the business contract can therefore create a larger performance improvement than a lower-level implementation optimization, but it must be an intentional product decision.
Inventory can sometimes be divided into independent quotas owned by regions. That moves coordination out of individual purchases while introducing quota rebalancing and temporarily stranded capacity. This is an architectural alternative for an appropriate business model, not a proof that global inventory was never difficult. Escrow-like allocation is revisited when the course reaches distributed transactions and geographic ownership.
Similarly, batching several changes can reduce synchronization overhead while increasing waiting time for an individual request. Deferring persistence can improve acknowledgment latency while changing the failure guarantee. Coalescing movement updates can reduce network traffic while preserving current position, but coalescing financial transfers without retaining their meaning can destroy required history. The workload contract determines whether an optimization is legal.
Case connection: a relational engine is a set of configurable contracts
PostgreSQL’s transaction-isolation documentation describes multiple isolation levels rather than one universal behavior. That makes it a useful example of a general principle: concurrency behavior is part of the interface and must match the invariant. The detailed locking and versioning mechanisms are developed in Lessons 20 and 21. The fact that a system supports transactions does not tell the application which observations a particular transaction may make.
The architectural analysis here is broader than that implementation fact. A general-purpose engine accepts a wide variety of queries, data relationships, and operational patterns. That flexibility creates work in planning, concurrency control, and administration. A specialized engine can constrain the workload to remove some of that work. The benefit is credible when the constraint matches the application; the limitation appears when requirements cross the chosen boundary.
A document database, an analytical engine, an embedded library, and a realtime database should therefore be compared against an explicit workload matrix. A product label cannot establish isolation, durable acknowledgment, or scalability. Those properties must be tied to the operation, configuration, topology, and failure model being discussed.
Trace the flash sale as a sequence of state transitions
A complete workload description can be made concrete without committing to a database product. Consider a sale with 2,000 units and 10,000 buyers. Each buyer supplies an operation identifier that remains unchanged across retries. The authoritative facts are the remaining stock, the set of accepted orders, and the stored outcome for each operation. The invariant is stronger than “stock is nonnegative”: the initial stock must equal remaining stock plus the number of units in accepted orders, excluding explicitly modeled replenishment and cancellation transitions.
The operation first determines whether its identifier already has a recorded outcome. A duplicate returns that outcome rather than competing for another item. A new operation conditionally allocates a unit, records an order if allocation succeeds, and records its final business result. These changes need one transaction or another protocol providing equivalent guarantees. If allocation succeeds but recording the order fails, an abort must undo the allocation. If the transaction commits and its reply disappears, replaying the identifier returns the committed order. The exact concurrency mechanism is postponed until later lessons; the required observable behavior is already precise.
This definition also separates rejection from failure. “Sold out” is a successful evaluation of a business rule. A malformed request is a validation rejection. An unavailable authoritative database is an infrastructure failure. A client timeout is an uncertain observation, because execution may still be progressing. Combining these outcomes into one error percentage hides what the service is doing and makes capacity planning unreliable.
Suppose the first 2,000 accepted orders arrive in two seconds. A system could meet the ownership invariant while forcing every unsuccessful buyer to wait a minute. It could also process rejections rapidly while preferentially admitting nearby clients. Fairness is not implied by transactional correctness. If the product promises first-come service, it must define arrival at a particular authoritative boundary and describe tie-breaking, retries, and connections that fail before acknowledgment. Those product decisions influence queueing and sequencing mechanisms.
Design conclusion
The first design artifact for a database is a workload and correctness contract. It identifies useful business outcomes, allowed observations, failure behavior, skew, resource demands, and measurement boundaries. Without it, “low latency” and “large concurrency” remain ambiguous goals that competing architectures can satisfy in incompatible ways.
The next lesson places those operations on actual hardware and operating-system boundaries. The workload contract tells us what must happen; the cost model explains where time and resources are spent making it happen.