learning_tla/lamport_video/docs/lecture08-2.md
2025-02-17 18:08:25 +01:00

3.2 KiB

8.2 Implementation - how it works

Specs implementing other specs

For TCommit the specification is TCSpec == TCInit /\ [][TCNext]_<<rmState>>, which we can simplify as TCSpec == TCInit /\ [][TCNext]_rmState

Module TwoPhase contains

  • INSTANCE TCommit, importing that module, including the definition of TCSpec.
  • THEOREM TPSpec => TCSpec, which asserts that for every behavior, if the behavior satisfies TPSpec then it also satisfies TCSpec.
    • This is what TPSpec implements TCSpec means

We can check it by adding TCSpec as a property to check on TwoPhase.

How can this theorem make sense: THEOREM TPSpec => TCSpec ?

  • TPSpec is an assertion about behaviors whose states assign values to four variables: rmState, tmState, tmPrepared and msgs.
  • TCSpec is an assertion about behaviors whose states assign values to one variable:rmState.

A state is an assignment of values to ALL variables, not just those in the module defining them.

So when evaluating TCSpec during the theorem evaluation, TLC will verify that rmState is unchanged, but not verify the other variables.

This seems weird when thinking of specs as programs. But they are not.

A specification does NOT describe the correct behavior of a system.

It describes a universe in which the system AND its environment are behaving correctly.

Stuttering

THEOREM TPSpec => TCSpec makes sense because both formulas are assertions about the same kind of behavior.

  • TPSpec == TPInit /\ [][TPNext]_<<...>>
    • TPNext includes TMAbort specs
      • TMAbort == tmState = "init" /\ tmState' = "done" /\ msgs' = msgs \cup {[type |-> "Abort"]} /\ UNCHANGED <<rmState, tmPrepared>>
      • meaning that these only allows states that leave rmState unchanged
  • TCSpec == TCInit /\ [][TCNext]_rmState
    • all TCNext steps change rmState
    • So TMAbort cannot be a TCNext step since it changes rmState

If steps leaving rmState unchanged were not allowed by TCSpec, the theorem would not be true, because TMAbort only contains states that leave it unchanged.

This is why we always add the [ ]_<<vars>> in the spec formula: to allow all steps outside those we want to verify, that do not change what the formula is about. These are called stuttering steps.

ALL TLA+ specs allow stuttering steps. If they didn't, any spec would allow the value of whateverUnrelatedVariableYouWant to change only when the protocol took a step. Which is a major case of aggrandizing.

But fundamentally the important part is that implementation IS implication.

Math simplicity is not an end in itself, but is a sign that we are doing things right.

Termination and stopping

A terminating or deadlock execution is represented by a behavior that end in an infinite sequence of stuttering steps: the universe keeps going after the system terminates.

All the specs we've seen so far allow a system to stop at any time, by taking infinitely many stuttering steps.

Specs specify what a system MAY do, not what it MUST do. They allow it to do nothing.

We add temporal requirements by conjoining (/-ing) a temporal formula to the specification, but that is not always necessary: the examples like two-phase commit or Paxos only use "may".