research27 min studyLesson 971 in Engineering

Specify service states and the transitions that are actually atomic

Turn a reservation contract into explicit state, guarded actions, unchanged fields, and environment behavior before checking correctness.

On this page

What you’ll understand

  • Define a finite service state and initial-state predicate.
  • Distinguish an atomic model action from an implementation's multi-step execution.
  • Trace guarded reservation, commitment, cancellation, and duplicate-request behavior.

A service contract describes more than successful inputs and outputs. It must explain what can happen between requests, after a retry, or when one participant stops. A state-machine specification makes those choices explicit through initial states and allowed transitions. Its usefulness depends on choosing an atomic boundary that the implementation can justify. Hiding a crash window inside one convenient action can prove a model correct while excluding the production failure that matters.

Choose state that preserves the contract's distinctions

Consider a service that allocates a fixed number of one-unit reservations. State includes a free-unit counter and a status for each stable request identifier: new, held, committed, or cancelled. Committed reservations consume units permanently in this small model; cancellation refunds only a held reservation. The initial state gives every identifier status new and sets free capacity to the declared total. Preserve terminal statuses instead of reusing identifiers, because forgetting an earlier commitment changes how retries behave. This model deliberately excludes replenishment, variable-size requests, and garbage collection.

A state is a valuation of all declared variables, not an informal diagram label. Specify their domains and the relationship between the counter and request statuses. If a response or durable record affects the promised contract, represent it rather than assuming it follows automatically from an internal status change. An abstract committed state can be useful, but a separate refinement argument must connect it to the actual storage and response boundary. Writing down the variables often reveals that two engineers meant different things by 'the request succeeded.'

F+∣H∣+∣K∣=C,F∈{0,…,C}F+|H|+|K|=C,\qquad F\in\{0,\ldots,C\}
C is a nonnegative integer capacity in one-unit reservations; F is free capacity, H the held-request set, and K the committed-request set. H and K are disjoint status classes over stable identifiers. This conservation relationship is a property to establish, not an automatic consequence of declaring the variables.↔ Scroll wide equations horizontally.

Actions are relations between complete states

Reserve(r) requires a new identifier and at least one free unit; it changes that identifier to held and subtracts one free unit. Commit(r) changes held to committed without changing free capacity. Cancel(r) changes held to cancelled and returns one unit. State explicitly that all unrelated statuses remain unchanged. The next-state relation permits any enabled action, so concurrent clients appear as different possible interleavings of these atomic transitions. A model does not prefer the helpful action unless a separate scheduling or fairness condition says so.

Duplicate calls need a defined result. A retry of an already committed identifier may return its recorded result while leaving state unchanged; it must not reserve another unit. Reusing the same identifier with a different payload should have a declared rejection or conflict rule. In TLA-style specifications, primed variables denote successor-state values, and stuttering permits a step that changes no modeled variable. PlusCal can generate such a specification from labeled algorithm steps, but its labels determine atomic boundaries that still need review against the intended service mechanism.

Reserve⁡(r):status⁡(r)=new ∧ F>0,F′=F−1,H′=H∪{r},K′=K\operatorname{Reserve}(r):\quad \operatorname{status}(r)=\mathrm{new}\ \land\ F>0,\qquad F'=F-1,\quad H'=H\cup\{r\},\quad K'=K
This mathematical action summary assumes r moves from new to held, all other request statuses stay unchanged, and all other state fields are framed explicitly. Primes denote the next state. The guard is part of the same atomic model transition as the updates.↔ Scroll wide equations horizontally.

Model interference at the boundary where it can occur

If the implementation checks capacity and updates it in different transactions, split those operations into separate model actions with intermediate state. Otherwise two workers can both observe one free unit before either decrements it, a behavior excluded by the atomic Reserve action. Likewise distinguish volatile and durable state when restart can erase one but preserve the other. Model message loss, duplication, or delay only where the environment permits them. Stronger environmental assumptions can simplify a model, but they narrow what the resulting analysis says about the deployed service.

Use the queue instrument to see why aggregate state is an incomplete service specification. Reset, set arrival λ=0 and service μ=2, predict that its initial backlog four becomes two after one second, then take two Steps. Separately Reset, set λ=2 and μ=2, predict backlog four after one second, then take two Steps. This fluid counter contains no identifiers, reservation ownership, durable records, or atomic transactions. Matching its aggregate count cannot establish the reservation contract; the missing state distinctions must be represented in the actual specification.

Follow capacity through commitment and cancellation

A service has capacity two and fresh identifiers A, B, and C. Starting from its initial state, execute Reserve(A), Reserve(B), Commit(A), Cancel(B), then Reserve(C). Find free, held, and committed counts, and determine whether Reserve(C) would have been enabled before Cancel(B).

  1. After reserving A and B, free capacity is zero and held count is two. Reserving fresh C at this point is disabled because its guard requires F>0.

  2. Commit(A) changes the counts to free zero, held one, committed one. Cancel(B) then returns B's held unit, producing free one, held zero, committed one.

  3. Reserve(C) consumes the available unit, leaving free zero, held one, committed one. Their sum is still two. A later retry of Commit(A) must not consume another unit under the stated duplicate policy.

The result

The final count triple is (F,|H|,|K|)=(0,1,1). Every transition relies on an explicit guard, status change, and frame condition.

Explore the model

Interactive lab

Interactive lab · queue

An aggregate counter omits request ownership

Reset, set λ=0 and μ=2, predict backlog 2 at t=1 s, then take two Steps. Separately Reset, set λ=2 and μ=2, predict backlog 4 at t=1 s, then take two Steps. The model is max(0,4+(λ−μ)t); controls pause while retaining time, so Reset each comparison. This fluid analogy has no service state machine or atomicity checker.

An aggregate counter omits request ownershipFluid backlog starts at 4 and follows max(0, 4 + (λ − μ)t). The separate M/M/1 residence-time formula assumes Poisson arrivals, exponential service and λ < μ. Current readings: Fluid backlog: 4; Net rate · λ − μ: −1 /s; M/M/1 mean residence: 1 s. All parameters are adjustable with the labeled controls below.02.557.51001.534.56Elapsed time · sFluid backlogλ < μ
Fluid backlogCurrent time
Fluid backlog
4
Net rate · λ − μ
−1 /s
M/M/1 mean residence
1 s
t = 0 s

Fluid backlog starts at 4 and follows max(0, 4 + (λ − μ)t). The separate M/M/1 residence-time formula assumes Poisson arrivals, exponential service and λ < μ. Parameter changes pause playback and recompute the model at the current elapsed time. Reset restores both parameters and returns time to zero. Playback starts paused. Adjust either parameter, then choose Play or Step.

Look forA useful specification includes distinctions that aggregate measurements cannot recover.

Check your understanding

The implementation reads capacity and later updates it without a shared atomic mechanism. How should the model represent that behavior?

Try it yourself

With capacity three, two held requests and no committed requests leave one free unit. Commit one held request and cancel the other. Find the resulting counts and explain why a second cancellation of the same identifier must not refund again.

Take a moment to make a prediction or work it out on paper. Open a hint when you need one.

Give me a hint

Commitment moves one unit between occupied status classes; cancellation moves one held unit back to free capacity.

Compare with the solution

The final counts are free two, held zero, committed one, totaling three. A second cancellation must return the recorded terminal result or be rejected without changing capacity. Refunding again would produce free three plus committed one, inventing a fourth unit and violating the conservation contract.

Take it with you

  • Initial states, domains, guards, updates, and unchanged fields define the machine.
  • Atomic actions require an implementation justification or a finer model.
  • Stable identities and explicit environment behavior make retry and failure claims testable.

Read further

Original sources and deeper explanations to follow at your own pace.

Optional progress, saved in this browser when available. Revisit any lesson at any time.

Lesson not marked complete.