I'm looking for some example specifications of an eventually consistent object store, such as amazon S3.
Andrew didn't notice that the conjunct
/\ ~has_entry(store', path)
is not "somewhat superfluous". It is completely superfluous because
it's implied by the preceding conjunct
/\ store' = [p \in (DOMAIN store \ path) |-> store[p]]
and the definition of has_entry. More precisely, it would if you had
written that preceding conjunct correctly. I don't remember what the
precedence of the operators is, so I don't know how
DOMAIN store \ path
is parsed. I presume you meant to write
DOMAIN (store \ path)
But S \ T is the set of elements in S but not in T, so the expression
you wanted is
DOMAIN (store \ {path})
To give you a more general answer, the next-state relation specifies
the new values of variables. A postcondition is a condition that
should hold on the values of the new variables. It should be implied
by the next-state relation and whatever precondition you assume about
the old values of the variables. As Andrew pointed out, sometimes you
can make the desired postcondition a conjunct of an action to rule out
some possible new values of variables that the rest of the action
allow.
----
TLC will check if a formula Inv is an invariant of the specification,
meaning that Inv is true on all reachable states. If you want to
put the assertion that Inv is an invariant in the specification, you
can write
THEOREM Spec => []Inv
where Spec is the TLA+ temporal formula that constitutes the
system's specification. However, TLC does not check THEOREMs.
----
As Andrew indicated, if you want to add a concept of time to your
spec, you should add a variable that represents a clock. If you want
your specification to model real time, see my article "Real Time is
Really Simple" at
http://research.microsoft.com/en-us/um/people/lamport/pubs/pubs.html#real-simple
You need real time if you are interested in properties such as "if X
happens then Y happens within 5 seconds". However, if you just need
properties such as "if X happens then Y eventually happens", then you
don't want to introduce time. You should instead learn about temporal
logic, liveness, and fairness--e.g., in Chapter 8 of "Specifying Systems".
Leslie
Hi Steve, all good questions.
1. Defining now()
Monotonically-increasing variables make model-checking difficult, because absent restrictions this means the system has an infinite number of possible states and so the model checker will never halt. My advice here is to think carefully about whether a monotonically-increasing global clock is really required for you to achieve the desired semantics. If you absolutely need the global clock, define it thus:VARIABLE GlobalClock
TypeInvariant == GlobalClock \in Nat
Init == GlobalClock = 0
Tick == GlobalClock' = GlobalClock + 1
Model-checking this is difficult. You'll have to define Nat to be a finite set like 1 .. 100, which means there exist behaviors which probably don't conform to the real world system - like an uninterrupted series of Tick relations until you hit GlobalClock = 100, after which other actions take place. You can mitigate this by not allowing Tick if other actions are enabled, but really this whole approach is messy and you're better off without a monotonically-increasing global clock. Why do you need one?
You understand that spec defines a subset of all behaviors such that Init is true in the first state, and for all steps either the ExampleAction boolean formula is true or it is a stuttering step.you aren't saying "if x is 0 or 1, then assign 1 - x to x after this action executes". You're saying "the ExampleAction formula is true of a step in a behavior if in the first state x is either 0 or 1, and in the second state x' = 1 - x". This isn't pedantry; moving from the state machine paradigm to the behavior paradigm is critical for understanding the temporal logic component of TLA+. The purpose of a spec is to define a set of correct system behaviors, where a behavior is a sequence of states and a state is an assignment of values to variables. So when you see this:It's best to view primed variable "assignments" and postconditions as one and the same. This is tricky to understand, but when you have an action like this:2. Specifying postconditionsExampleAction ==/\ x \in {0, 1}/\ x' = 1 - xInit == x = 0
Spec == Init /\ [][ExampleAction]_<<x>>
How does this all relate back to your question? Simply, a postcondition of the type you've given is perfectly fine. You can use primed variables in whatever logical statements and checks you want and it will not create a new outcome. What adding conjuncts can do is remove outcomes. For example, if we have the following:ExampleAction ==/\ x \in {0, 1}/\ x' = 1 - x/\ x' \in {2, 3, 4}
Here we have the "postcondition" check that x' is in the set {2, 3, 4}. It's impossible for ExampleAction to be true of any step, since the conjuncts are clearly contradictory and can never all be true. Tying this back to your example, we have these conjuncts:
/\ store' = [p \in (DOMAIN store \ path) |-> store[p]]
/\ ~has_entry(store', path) \* HERE
If the first conjunct is true (which will probably always be the case) and the second conjunct (your postcondition) is not, TLC will simply not take this step. Personally I would use the first conjunct as the postcondition in and of itself.
A minor stylistic note: rather than modifying the domain of your store variable, consider mapping undefined paths to a placeholder null value instead.
3. InvariantsI'm not sure I understand this question. Could you specify what doGet and doHead do, and what it means for them to be consistent?
I really want set a wall time when objects are created.
I really want set a wall time when objects are created.In your original post you said you wanted to be able to model that for example ~GET(path), PUT(path), GET(path) wasn't guaranteed to get what you put. What else do you need to model, that you'd need a wall time for?
I'm trying to set a created date on files.
It's interesting as it complicates testing; you can't execute that exact sequence to demonstrate that the store appears to have create consistency, or if you do, you'd better have some idea of what a good delay between the negative GET and the PUT should be. And it'd have to be a coded in delay, as the usual probe+sleep strategy doesn't work, as it just refreshes the entry in the cache.