14.5 Vector Time
(February 2008)
Site identifiers
It is assumed that each site is uniquely identified with a site identifier. Let S be the set of site identifiers.
Vector times
Subset, intersection and union on vector times
Note
- ∀v, v ↓ v = v
- ∀v, v ↑ v = v
- ∀v1,v2, v1 ↓ v2 = v2 ↓ v1
- ∀v1,v2, v1 ↑ v2 = v2 ↑ v1
- ∀v1,v2,v3, (v1 ↓ v2) ↓ v3 = v1 ↓ (v2 ↓ v3)
- ∀v1,v2,v3, (v1 ↑ v2) ↑ v3 = v1 ↑ (v2 ↑ v3)
- ∀v1,v2,v3, (v1 ↓ v2) ↑ v3 = (v1 ↑ v3) ↓ (v2 ↑ v3)
- ∀v1,v2,v3, (v1 ↑ v2) ↓ v3 = (v1 ↓ v3) ↑ (v2 ↓ v3)
- ∀v, v∅ ≤ v
- ∀v, (v∅ ↑ v) = v
- ∀v, (v∅ ↓ v) = v∅
- ∀v1,v2, (v1 ↓ v2) ≤ v1
- ∀v1,v2, v1 ≤ (v1 ↑ v2)
- ∀v, v ≤ v
- v1 ≤ v2 & v2 ≤ v1 ⇒ v1 = v2
- v1 ≤ v2 & v2 ≤ v3 ⇒ v1 ≤ v3
- v1 ≤ v & v2 ≤ v ⇒ (v1 ↑ v2) ≤ v
Identification of atomic operations
Each atomic operation is assumed to be generated at exactly one site. Each site independently assigns a zero based sequence number t to each atomic operation generated at that site.
Extent of a vector time
This definition relates to the whole purpose for vector times - for a given atomic operation O, we describe its execution context using the vector time v satisfying ec(O) = χ(v). This is a summary of the complete set of atomic operations that have been executed prior to O.
Note the following
- χ(v2) \ χ(v1) = { op(s,t) | v1(s) ≤ t < v2(s) }
- χ(v∅) = {}
- χ(v1 ↑ v2) = χ(v1) ∪ χ(v2)
- χ(v1 ↓ v2) = χ(v1) ∩ χ(v2)
- v1 ≤ v2 ⇔ χ(v1) ⊆ χ(v2)
Precedes relation on atomic operations
Let Oc be generated on site C.
Ob → Oc so Ob was executed on site C before Oc was generated.
By causality preservation and Oa → Ob, we know that Oa is executed before Ob at all sites, and in particular at site C.
Therefore Oa was executed at site C before Oc was generated at site C, so we deduce Oa → Oc.
Execution context of an atomic operation
It follows from the definitions that
Oa → Ob ⇔ Oa ∈ χ(gc(Ob))
It is assumed that a site generates operations in the context of previously generated operations at that site. Formally this means
t1 < t2 ⇔ op(s,t1) → op(s,t2)
Suppose Oa || Ob and Oa.s = Ob.s.
Aggregate intersection and union on sets of vector times
Causally valid vector times
or equivalently
Taking the negation, vector time v breaks causality if
∃operations O1,O2 st O1 ∉ χ(v) and O2 ∈ χ(v) and O1 → O2
Each site is assumed to have a linear sequence of operations that have been executed at that site called a history buffer. For a given site, let vh denote the vector time whose extent describes the current contents of the history buffer.
Let v be a causally valid vector time satisfying v ≤ vh. It can be proven that it is possible to transpose adjacent, concurrent operations within the history buffer to separate it into a prefix and suffix such that the set of operations in the prefix equals χ(v) and the suffix corresponds to χ(vh) \ χ(v). Furthermore no atomic operation in the suffix causally precedes an atomic operation in the prefix.
More specifically it won't be necessary to ever transpose a pair of contextually serialised operations [O1, O2] where O1 → O2.
Let v = v1 ↓ v2. Let O1,O2 be operations with O2 ∈ χ(v) and O1 → O2. Need to show O1 ∈ χ(v).
Let v = v1 ↑ v2. Let O1,O2 be operations with O2 ∈ χ(v) and O1 → O2. Need to show O1 ∈ χ(v).
It follows therefore that
∀ v ∈ V, v is causally valid ⇒ ↓(V) and ↑(V) are causally valid
Formulation that allows more sites over time
Usually a vector time is defined with respect to a fixed number of sites in the system. In practice we need a definition that naturally allows for new sites to be added over time.
It is straightforward to adjust the previous definitions to avoid reference to some fixed set of states S.
v1 ≤ v2 ⇔ ∀ s ∈ sites(v1), v1(s) ≤ v2(s)
For vector times v1,v2, v = v1 ↑ v2 denotes the vector time satisfying
sites(v) = sites(v1) ∪ sites(v2)
∀s ∈ sites(v), v(s) = max( v1(s), v2(s) )
For vector times v1,v2, v = v1 ↓ v2 denotes the vector time satisfying
sites(v) = sites(v1) ∩ sites(v2)
∀s ∈ sites(v), v(s) = min( v1(s), v2(s) )
Delta vector times
A delta vector time represents a change to a vector time which can be useful to reduce network bandwidth.
and sites(Δv) = { s | (s,t) ∈ Δv }
We define v1+Δv as follows.
(⊆)
so (s,t) ∈ ( { (s,t) ∈ v1| s ∉ sites(v2-v1) } ∪ { (s,t) ∈ (v2-v1) | t > 0 } )
if (s,t) ∈ (v2-v1) and t > 0
s ∈ sites(v1) (because (s,t) ∈ v1)
t > 0 (because (s,t) ∈ v1)
(s,t) ∉ (v2-v1)
if s ∉ sites(v2)
s ∈ sites(v2-v1) ⇒ contradiction
Suppose v1(s) = t ≠ v2(s)
so s ∈ sites(v2-v1) ⇒ contradiction
so (s,t) ∈ v2
if t ≠ v1(s)
t > 0 (because (s,t) ∈ v2)
(s,t) ∈ v1 + (v2-v1)
so (s,t) ∈ v1
Suppose s ∈ sites(v2-v1)
⇒ contradiction
So (s,t) ∈ { (s,t) ∈ v1 | s ∉ sites(v2-v1) }
So (s,t) ∈ v1+( v2-v1)
We can define addition of delta vector times Δv = Δv1+ Δv2, satisfying (for any given vector time v)
(v + Δv1) + Δv2 = v + (Δv1+ Δv2)
Note that addition of delta vector times doesn’t commute.
It can be show that
Δv1+ Δv2 = Δv2∪ { (s,t) ∈ Δv1| s ∉ sites(Δv2) }
Vector time implementation
To facilitate fast look up of a vector time, a suitable implementation is a red-black tree. However a delta vector time has no need for fast look up, and therefore may be more efficiently stored using a variable size array of (s,t) pairs.
We can define an add method on a vector time in pseudo code as follows
v.add(s,t)
{
if (v.hasentry(s))
{
v.remove(s);
}
if (t > 0)
{
v.insert(s,t);
}
}
Applying a delta to a vector time simply involves calling add(s,t) for each (s,t) in the delta.
We can define an add method on a delta vector time in pseudo code as follows
Δv.add(s,t)
{
if (Δv.hasentry(s))
{
Δv.remove(s);
}
Δv.insert(s,t);
}
This may lead to a delta that has more (s,t) entries that it needs - because it doesn’t check for redundancy w.r.t the original vector time to which the delta applies.
Related work
The causally valid vector times in this chapter correspond to what the distributed-systems literature commonly calls consistent cuts. Under this correspondence, the extent χ(v) is the set of events in the cut, and the componentwise ordering of vector times is the inclusion ordering of their extents.
Mattern shows that consistent cuts form a sublattice of the lattice of all cuts [2]. In particular, consistent cuts are closed under infimum and supremum. This gives the standard literature counterpart of the claims above that causally valid vector times are closed under ↓ and ↑. Garg and Mittal likewise formulate the consistent cuts of a distributed computation as a distributive lattice [3]. The terminology and χ(v) notation used in this chapter are specific to the present treatment, but the underlying closure result is standard.
References
- Leslie Lamport, Time, Clocks, and the Ordering of Events in a Distributed System, Communications of the ACM, 21(7), pages 558–565, 1978.
- Friedemann Mattern, Virtual Time and Global States of Distributed Systems, 1989.
- Vijay K. Garg and Neeraj Mittal, On Slicing a Distributed Computation, 2001.
Source code
The surviving 2008 C++ implementation is retained as a documentation resource. The source browser also
includes TestVectorTime.cpp, a comprehensive unit test written for the refactored
TVectorTime implementation in 2019; it is included as later supporting material and is not
part of the original 2008 source.
Browse the source files.