Computer Science Computer Science major 100% Free Open Access
Chapter 2 • Theory & Derivations

Coordination

Lamport logical clocks and event ordering, Vector clocks and causal ordering, Distributed mutual exclusion and leases, Leader election and authority, Capstone: causal collaboration under intermittent connectivity

§2.1 Lamport logical clocks and event ordering

A physical clock estimates elapsed real time; a logical clock records information about the order in which events could influence one another. In a distributed program there is often no trustworthy globally observed order for two independent events. Leslie Lamport's happened-before relation supplies a partial order: an earlier event within the same process precedes a later event, sending a message precedes its reception, and the relation is transitively closed. Two events are concurrent when neither happened before the other. Concurrency does not assert that events occurred at precisely the same instant. It says that the recorded communication structure does not order them. An integer Lamport clock is maintained by incrementing on local events and sending timestamps with messages. On receiving timestamp t, a process sets its counter to max(local,t)+1. This gives the crucial implication: if a happened-before b, then C(a)

The distinction between implication and equivalence is foundational. Let an event in process P have clock 12 and an independent event in Q have clock 15. Clock order alone does not show that the event at P could influence Q; the larger number may reflect many local operations at Q. A total order can be constructed by comparing pairs (clock value, unique process identifier), but this order includes arbitrary choices between concurrent events. Such choices can be useful for deterministic tie-breaking or log sorting, yet they are not evidence of real-world causality. A server processing shopping-cart updates can use Lamport tags to order operations reproducibly, provided every replica uses the same comparison, but the ordered result may still require domain-specific conflict resolution. Logical clocks solve a bookkeeping problem, not a transactional isolation problem.

Trace a three-process exchange. P starts at clock zero, performs local event p1 and sends message m with timestamp 2. Q's local clock had reached 5 independently; after receiving m, Q must move to 6. Q replies with timestamp 7, and P receives at max(2,7)+1=8. A student who merely increments P to 3 upon reception has discarded information from the causal chain and violated the clock condition. The timestamps do not imply that P and Q were physically synchronized. If an external observer compares p1 with Q's prior independent event at timestamp 5, the relation may still be concurrent. To recover which events depended on which, retain send/receive edges or a richer clock structure. Distinguish a recorded causal edge from a user-intended dependency; an application may require an explicit reference such as reply-to to represent conversational meaning.

Integer clock overflow, service restarts, and membership changes matter when implementing a system rather than a classroom toy. A process counter that resets after restart can reuse timestamp pairs unless an incarnation identifier or durable epoch differentiates generations. Identifiers used for deterministic total ordering must themselves be stable and unique within the relevant scope. The event rate also affects storage size if every event carries tags. Some services choose log sequence numbers assigned by a coordinator, which serialize at an authority; others use hybrid logical clocks that combine physical-time estimates with logical disambiguation under specified assumptions. Avoid treating hybrid clocks as a universal substitute for coordination. They can help order writes but cannot by themselves decide whether two disconnected writers both sold the same unique asset.

Implement a clock trace with two process counters and a channel carrying stamped messages. Give controls for local event, send, delayed delivery, and receive. Show the current integer at each process and draw edges from sends to receipts. The first task is to confirm the clock condition for every happened-before edge. The second is to find a pair of events with ordered clock integers but no causal path. Then intentionally turn off the max-on-receive update and observe the incorrect trace. Add a restart event and ask whether old and new timestamp pairs are still uniquely identifiable. This model illustrates event bookkeeping only. It makes no assumption that the displayed animation time equals process time or that every network message arrives eventually.

§2.2 Vector clocks and causal ordering

Vector clocks enrich an event timestamp with a component for each tracked process. Initially each process owns a vector of zeros. Before recording a local event, process i increments component i. On receiving vector V, it merges each component with the maximum of its own and V, then increments its own component for the receive event. For two timestamp vectors A and B, A precedes B componentwise when every coordinate of A is less than or equal to B and at least one is strictly smaller. If neither vector precedes the other, they are incomparable and represent concurrent events under the standard assumptions of fixed membership and correctly maintained clocks. Unlike a scalar Lamport clock, vector comparison can therefore recover concurrency from timestamps. The cost is metadata proportional to the number of tracked actors.

Consider three replicas with vector [2,0,0] at A and [0,1,0] at B. Neither vector dominates the other, so the recorded updates are concurrent. If B later receives A's update and then edits the object, its vector becomes at least [2,2,0] and causally succeeds both local and received histories. A storage service may maintain dotted version vectors, dependency sets, or other compressed variants for practical scale. Nevertheless it must define what an actor is and how that actor is retired. A thousand client processes with short lifetimes make naive per-client vectors burdensome. Version metadata also does not resolve semantic conflicts: concurrent bank transfers cannot safely be merged with set union just because their timestamps are incomparable. The data type and invariants determine whether merging is legal.

A causal feed example clarifies the usefulness. A researcher posts a dataset, then posts a correction referring to that dataset. A collaborator should not see the correction before the original item if the application promises causal delivery. Yet two independent researchers can post unrelated datasets concurrently; the system may present those in either order. A causal broadcast layer can hold a message until its dependencies have been delivered locally. This may delay presentation if a needed ancestor is lost or blocked, so applications need retention and recovery plans. Note that 'message B references A' is an application relation; causality tracking must actually receive or encode A's dependency to honor it. Merely assigning wall-clock dates to posts does not solve missing ancestor delivery.

Comparing vectors also exposes the limits of simplifications. The common rule 'choose the numerically largest vector' is meaningless, because there is no single scalar maximum when vectors are incomparable. Summing coordinates is worse: concurrent vectors [4,0] and [0,5] have unequal sums but no causal relation. Merging vector components represents the union of observed causal histories, not an automatic merge of application states. If two replicas concurrently remove and add a collaborator, the desired outcome depends on the specification: add-wins sets, remove-wins sets, observed-remove sets, and explicit conflict prompts encode different product decisions. Some compact causality schemes shift storage burden into a centralized version service; examine whether that choice undermines the desired availability under partition.

Use a three-actor simulation in which each local event increments one vector entry, send attaches the vector, and receive merges and increments. Display vector timestamps next to events and allow learners to pick two nodes for comparison. A green arrow means one vector dominates the other; a split icon denotes concurrency. Ask why vectors [3,1,0] and [2,2,0] are incomparable, then add a message that causes one event to follow the other. Test the invariants that local components never decrease and received information is never lost. Include a mode that treats a process restart as the same actor and discuss the risk when durable counters are not restored. The simulation is intentionally limited to static membership, not arbitrary actor garbage collection.

§2.3 Distributed mutual exclusion and leases

Mutual exclusion means that at most one participant occupies a critical section protected by a particular coordination contract at a time. On a single machine, a mutex often relies on shared memory and scheduling primitives. Across machines, participants exchange messages with delay and possible loss, and a failed lock holder may not immediately release its resource. A simplistic remote lock stored as a boolean can be unsafe if a lease expires while the holder is merely paused. After resuming, the former holder may continue writing even though a new holder legally acquired the lease. Fencing tokens address this hazard by assigning monotonically increasing epochs to successive owners; the protected resource rejects operations carrying an epoch older than the largest one it has accepted. A lock server without enforcement at the resource is not a complete correctness design.

Imagine workers A and B updating a shared report on object storage. A obtains lease epoch 41 and begins a large computation. A is paused for fifteen seconds; the lease expires and B obtains epoch 42. B writes the new report. A resumes and attempts to overwrite the report using stale state. If the storage interface blindly accepts writes, the existence of a lease did not preserve mutual exclusion. If the storage layer compares fencing tokens and rejects epoch 41 after observing 42, the stale writer cannot corrupt the newer result. The monotonic property needs a durable authority and handling of leader failover. Do not use independent per-process counters as tokens; two leaders may issue the same number after a split. The concrete security property is enforced at the final resource, not at the caller's memory of the lock.

Permission-based algorithms such as Ricart–Agrawala order requests using Lamport timestamps and require coordination among participants. Token-based algorithms circulate a unique permission token. A centralized coordinator grants locks and may become a performance and recovery dependency. Each family has costs in message complexity, latency, membership changes, and fault tolerance. Requesting permission from every process in a system of thousands can be expensive and vulnerable to unresponsive members. For leader-based lock services, a quorum-backed authority can preserve uniqueness while sacrificing availability when quorum is unavailable. Not every task needs a distributed mutex: partitioning workload by key, using database conditional updates, or making operations commutative often eliminates the need for a global critical section. Removing contention can be a better design than optimizing coordination messages.

Lock semantics should be expressed in terms of an invariant and a recovery policy. Does a lock prevent two workers from attempting work or from committing conflicting outputs? Must it survive the holder's crash? Can a legitimate long job extend its lease, and what if renewals fail while its effect is already visible? A lease can be a useful efficiency mechanism while stronger transaction preconditions protect the data. The distinction matters in migration workflows, where two reconcilers may both calculate a safe transformation but only one should install it. Operational dashboards should expose owner epoch, renewal latency, abandoned work, and rejected stale token counts. A spike in renewals during garbage collection can signal impending contention rather than prove that the distributed algorithm is broken.

Create a lease/fencing model with two workers, one resource, a lease duration, a pause duration, and an epoch counter. In the first scenario, A acquires epoch 7, pauses longer than its lease, B acquires epoch 8 and writes, then A resumes and writes without fencing. The final state should show the stale overwrite. In the second scenario, the resource rejects writes with epochs lower than its highest accepted epoch. Learners should vary pause duration and renewal delays, recording when a non-paused worker can still lose authorization. Include a deliberately incorrect variant that compares local wall-clock timestamps; demonstrate that clock skew makes that rule unsafe. This is a simplified model of lock ownership and storage enforcement, not a proof of any particular commercial lock API.

§2.4 Leader election and authority

A leader is a node that has been granted a specific authority within a coordination protocol. The existence of one leader-looking process does not guarantee unique authority across a network split. Correct systems usually associate leadership with a monotonically increasing term or epoch, and often with a quorum decision. The term identifies generations of authority and helps reject messages from old leaders. Some election schemes choose the participant with the highest identifier; others use randomized timeouts, log freshness checks, or explicit priority rules. A priority election by itself is not a replicated log consensus protocol. The safety question is whether conflicting leaders can both commit operations under the actual acknowledgement rule, not whether two machines temporarily display 'leader' on their dashboards.

Suppose nodes A, B, C replicate a configuration log. B loses contact with A and starts a new election. If B and C grant votes in term 12, B can become leader in a three-member group. The old leader A may still believe it leads term 11. Correct followers reject A's obsolete term and do not provide a majority that would let it commit fresh entries. The protocol must persist relevant term and vote information before acknowledging votes, or a crash/restart can forget promises and undermine election safety. The leader's log freshness matters too; electing a lagging candidate may threaten previously committed entries unless the protocol uses a suitable voting restriction and commit rule. Simplistic comparisons such as 'newest wall-clock timestamp wins' do not establish those properties.

Operational election timing involves a difficult tradeoff. If the heartbeat interval is short but the election timeout is also very short, transient pauses and network bursts trigger unnecessary elections, which stall writes and increase log divergence. If the timeout is too long, actual failures take longer to recover. Many systems randomize election deadlines to reduce simultaneous candidacies, but randomized backoff is a liveness aid, not a mathematical guarantee of progress under an infinitely adversarial network. Membership changes are especially delicate. Adding a new voter without a safe transition may create two disjoint groups that each regard themselves as a majority of different configurations. Production consensus implementations define joint consensus or another rigorous reconfiguration protocol. A simple 'append one more server to the list' operation is insufficient.

Leader election is sometimes unnecessary. Stateless HTTP services may use load balancing rather than a leader. Shard-local primary nodes can avoid a single global leader by giving each partition its own election group. Analytics aggregators may accept approximate duplicate work; an exclusive global coordinator would increase cost without improving a user-visible invariant. Conversely, transaction commit coordinators or ownership records may require a unique authority to avoid contradictory outcomes. When documenting election design, specify the voting members, durability of vote records, authority of non-voters, term handling, write acknowledgement threshold, and recovery from prolonged partition. Observe election frequency, term increments, follower lag, and the share of time the system lacks a writable leader. These reveal different causes of availability loss.

Build an election experiment for a three-node replicated log. Expose each node's current term, voted-for field, log last index, and role. Let learners partition A from B and C, advance logical time, and send vote requests with explicit terms. Require one vote per term per durable voter and include candidate log freshness rules. Show that two candidates cannot each obtain two votes from the same three voters in one term unless a voter violates the one-vote rule. Then crash a voter, restart it with deliberately discarded state, and illustrate how forgetting votes can produce invalid traces. The lab purposely does not implement all Raft append, snapshot, or membership-change rules; students must not equate passing election tests with a fully verified consensus implementation.

§2.5 Capstone: causal collaboration under intermittent connectivity

Build a note-sharing application for researchers working in a laboratory with intermittent Wi-Fi. Multiple users create notes, annotate references, and reply to discussion threads while offline. A central challenge is not simply storing multiple JSON documents; it is preserving useful relationships between edits. If a response refers to a note that has not yet arrived at a replica, showing the reply alone may confuse readers or accidentally disclose hidden context. The application can represent causal dependencies with event identifiers and vector-like version metadata. A note creation operation A followed by a reply B can declare that B depends on A. Two unrelated notes from different users may be concurrent and need not be globally serialized. The UX should show pending sync and conflict states honestly rather than claiming every device displays the same view at all times.

Start with two clients, Alice and Bob, and one server. Alice creates document D while offline and adds an annotation; Bob independently edits a previously synced paragraph. On reconnection, the server may receive Bob's update first even if Alice's local screen displays the new document as earlier. A scalar timestamp comparison cannot identify every causal relationship. The design can attach a dependency set to operations and delay displaying an operation until required ancestors are present. Vector clocks provide one way to represent partial ordering with limited membership. For a large population of ephemeral devices, a fixed vector entry per device is costly; per-document actor sets, version vectors, or server-assigned generations may reduce the burden. The metadata design should be chosen after estimating the number of active writers and the retention period for offline replicas.

Pick a concrete data type. A research tag list may merge safely using a set with documented add/remove behavior, while a free-form paragraph does not generally have a natural commutative merge. For text, specialized sequence CRDTs or operational transformation algorithms can preserve concurrent insertions under their assumptions, but the visible result may still require human editorial review. The paper's abstract, title, and author order may have different merge policies. The application should not use a single 'latest timestamp wins' rule for every field, because doing so could silently discard important changes. A user should be able to inspect who edited which field and how a conflict was resolved. Mathematical convergence and reader satisfaction are different success criteria.

Add a moderation boundary. An authenticated reviewer removes a note containing an unsafe secret while another offline editor still holds a prior copy. Eventual convergence alone does not guarantee immediate revocation on a disconnected device. This is a security and product constraint that the conflict-free data structure cannot erase. The server can restrict future synchronization, rotate access capabilities, and maintain an audit trail, but it cannot instantly delete bytes from an entirely disconnected and untrusted laptop. Make the threat model explicit: authorized collaborators who may be offline are not equivalent to fully trusted internal replicas. Some operations may need online confirmation, especially changing permissions or publishing externally. Others can remain local drafts until connectivity returns.

Test convergence and causal visibility separately. Generate a deterministic set of operations with dependencies, deliver them to replicas in different orders with duplicates, and compare final application states after all required messages arrive. Then inspect intermediate states: did a reply become visible before its parent note? Did a concurrent tag update survive? Did an unauthorized device manage to publish after revocation? The expected outcomes differ by feature, and a single equality hash does not capture all requirements. The capstone should end with a conflict policy table naming each field, allowed offline operations, merge semantics, and security checks. This makes a more useful artifact than a vague claim that vector clocks solve collaboration.

THREE WORKED QUESTIONS

Step-by-Step Programming Fundamentals Solutions

Three original questions connect calculations, definitions and interpretation. Open each solution to follow the reasoning.

Worked Example 2.1: Calculate a Lamport receive event

Process P has Lamport clock 4 when it receives a message carrying timestamp 11. What should P record for the receive event, and what does the value not prove?

Step 1: reasoning

The receive rule is C_new=max(C_local,C_message)+1, so the maximum of 4 and 11 is 11.

Step 2: reasoning

Increment after merging: C_new=12. This preserves the implication that the sending event precedes its reception.

Step 3: reasoning

The value 12 does not prove physical time, nor that every event stamped 10 causally preceded this receive. Scalar clock order has no converse guarantee.

Final Answer & Physical Insight

The new Lamport clock is 12; it is a causal bookkeeping value rather than an absolute timestamp.

Worked Example 2.2: Compare concurrent vector events

Two events in a system with three actors have vector timestamps A=[3,1,0] and B=[2,2,0]. Is either event causally before the other, under normal vector-clock assumptions?

Step 1: reasoning

To have A≤B componentwise, compare the first coordinate: 3≤2 is false.

Step 2: reasoning

To have B≤A componentwise, compare the second coordinate: 2≤1 is false.

Step 3: reasoning

Neither vector dominates the other. The two events are concurrent in the represented causal history, although their real-time occurrence could differ.

Final Answer & Physical Insight

The events are incomparable and therefore concurrent according to the vector-clock relation.

Worked Example 2.3: Reason about fencing epochs

Worker A holds epoch 71 but pauses. Its lease expires; worker B gains epoch 72 and updates the protected object. A then sends a write with epoch 71. What must the object storage layer do?

Step 1: reasoning

The storage layer remembers the highest accepted epoch for the object, now at least 72.

Step 2: reasoning

It compares incoming epoch 71 with 72 and rejects the stale write. Merely knowing that A had an old lease is not adequate if storage does not enforce the comparison.

Step 3: reasoning

If the layer accepted 71, the system could overwrite B’s legitimate value after newer ownership, contradicting the intended exclusive-authority contract.

Final Answer & Physical Insight

Reject A’s stale epoch-71 write after accepting epoch 72.