UCS1701 - Distributed Systems
UCS1701 - Distributed Systems
Consider a modern ad-hoc distributed system like vehicular or mobile networks. As the
characteristics of ad-hoc systems vary from the conventional distributed systems, the design
challenges of ad-hoc systems should be studied for renovating the insight towards key
technological shifts.
a. Adapt the conventional representation of vector clocks to suit the ad-hoc distributed system.
Dynamic Vector:
The number of processes in the system is not constant. So the vector cannot have a fixed
size. Maintain a counter variable and when a node joins the system, increment the counter
variable and store the clock value and process ID. When a process leaves, decrement the
counter, remove the clock value from the vector and shift all the values in the vector.
Stagnant Process:
Maintain a TTL value which when expired removes the corresponding process’s entry from
the vector clock. If any message exchange takes place involving that process, the vector
clock entry would be updated.
Causality:
Maintain a time window. The processes occurring within that window are
considered concurrent and not causal.
Renewing Process Clock Value:
When timestamp is passed, comparison has to be done with the values in the timestamp
and the values in the vector clock of the receiver process. This helps to check if outdated
processes are present in the vector clock. If any process’s entry has expired, but is present in
either the timestamp or the vector clock entry, it will get renewed.
b. Formulate the implementation rules for the adapted design of vector clock
synchronization concerning the design challenges of the ad-hoc distributed system.
All the processes should maintain a queue initially with its own identifier in the
queue and clock value. Vector is an array of structures and for process i will look
like [{i, 0, t, 1}] Ci.id = i, Ci.clock = 0 and Ci.ttl = t, Ci.counter = 1
Receiving Event:
Sender -> i
Receiver -> j
Other Processes -> k
Time stamp sent -> tm
Expired Process:
If Ci.id == k and Ci.ttl == 0, remove it from the queue, Ci.counter--.
c . Elaborate the working of the adapted design through a relevant example.
To simulate the working of the newly designed vector clock using the provided rules, we'll
create examples for various degrees of ad-hocness. The vector clock is represented as an
array of structures, where each structure holds the process ID, the clock value, the
time-to-live (TTL), and a counter. Let's explore the behavior of this vector clock in different
scenarios.
In this less ad-hoc scenario, the vector clocks remain straightforward, and the structure of
the processes is relatively stable.
In this moderately ad-hoc scenario, the vector clocks handle the dynamic nature of
the environment with occasional node departures and arrivals.
In this highly ad-hoc scenario, the vector clocks cope with the frequent changes in the network
structure, demonstrating their adaptability to rapidly changing conditions and maintaining event
ordering. The use of TTL ensures that expired processes are removed from the queue, helping
to manage the vector clock's size.
d. Code generated from Copilot/ ChatGPT for Conventional Vector Clock will be provided.
Perform the implementation for a, b and c
Python
class AdHocVectorClock:
self.process_id = process_id
self.clock = {process_id: 0}
self.clock[new_process_id] = 0
self.ttl_tracker[new_process_id] = ttl
print(f"Process {new_process_id} added to the vector clock.")
"""Removes a process from the vector clock when TTL expires or the
process leaves."""
if process_id in self.clock:
del self.clock[process_id]
del self.ttl_tracker[process_id]
def decrement_ttl(self):
"""Decrements TTL for all processes and removes them if TTL hits
zero."""
to_remove = []
self.ttl_tracker[process] -= 1
if self.ttl_tracker[process] <= 0:
to_remove.append(process)
self.remove_process(process)
def update(self, process_id):
if process_id in self.clock:
self.clock[process_id] += 1
def send_event(self):
self.update(self.process_id)
return self.clock.copy()
if process in self.clock:
else:
self.add_process(process)
self.clock[process] = timestamp
def get_clock(self):
"""Returns the current vector clock."""
return self.clock
def display_clock(self):
# Example Simulation
vc_A = AdHocVectorClock('A')
vc_B = AdHocVectorClock('B')
vc_C = AdHocVectorClock('C')
vc_A.send_event()
vc_A.display_clock()
vc_B.receive_event(vc_A.get_clock())
vc_B.display_clock()
# Step 3: C joins the network.
vc_A.add_process('C')
vc_B.add_process('C')
vc_A.display_clock()
vc_B.display_clock()
vc_A.send_event()
vc_A.display_clock()
vc_A.decrement_ttl()
vc_A.display_clock()
vc_B.decrement_ttl()
vc_B.display_clock()
vc_B.receive_event(vc_A.get_clock())
vc_B.display_clock()
e. Provide a theoretical proof that illustrates the working of proposed algorithm.
The adapted vector clock for ad-hoc distributed systems introduces dynamic processes with
varying lifespans (Time-to-Live) and allows for processes to join or leave the system arbitrarily.
The goal is to show that the vector clock maintains causality, preserves event ordering, and
adapts effectively to process dynamics. Here's a formal proof.
In a distributed system, the vector clock ensures that if an event e1 causally precedes event e2,
the vector timestamp of e1 will be smaller than that of e2. This can be written formally as:
For a process P, assume it sends an event e1 and then event e2 in that order. Each time an
event is sent, the process's clock is incremented. This guarantees that the local timestamp at P
for e1 is smaller than for e2:
Next, assume process P1 sends an event e1 to process P2. When P2 receives the event, it
updates its vector clock by taking the maximum of its current timestamp and the incoming one
for each process:
This ensures that if e1 occurs before e2, the vector clock at P2 is updated such that the
timestamp for e1 is always smaller than for any subsequent event e2.
2. Concurrent Events
Concurrent events are events that occur in different processes that do not causally affect each
other. In an ad-hoc distributed system, two processes P1 and P2 can send events
independently. Since these events do not have a causal relationship, their vector clocks will
reflect this independence:
If event e1 at P1 and event e2 at P2 are concurrent, the vector clocks are incomparable,
meaning neither is smaller or greater than the other:
𝑉(𝑒1)𝑃1∥𝑉(𝑒2)𝑃2
This property holds even with dynamic processes joining or leaving the system because the
vector clocks only maintain relevant process entries. When a process joins, it adds itself to the
clock without affecting the ordering of previous events. When a process leaves, its entry is
removed, but the event ordering of other processes remains unaffected.
The system dynamically adjusts the vector clock size by adding new processes upon joining
and removing them when they leave or when their TTL expires.
For a new process P3 joining the system, the adapted vector clock adds a new entry for P3 with
an initial timestamp of 0. This allows P3 to participate in event exchanges without affecting the
ordering of prior events.
If a process P3 leaves, it is removed from the vector clock. This removal is consistent with the
system’s requirement to maintain event causality, as the removal of P3 only eliminates stale
processes and their associated timestamps. The remaining processes still preserve the causal
relationships among themselves, as the vector clock comparison only focuses on relevant
(non-expired) processes.
Given that vector clocks rely on comparing timestamps across processes, we need to show that
this comparison is valid even when processes dynamically join or leave. For any two processes
P1 and P2, the comparison is valid as long as the vector clock contains entries for all active
processes. When P3 leaves, its timestamp is no longer relevant, and removing it does not affect
the comparison of events between P1 and P2.
𝑉(𝑒1)𝑃1[𝑖]≤𝑉(𝑒2)𝑃2[𝑖], ∀𝑖
Where i is any active process at the time of the event comparison. The TTL mechanism
ensures that stale processes are removed and the vector clock remains a manageable size
without impacting the correctness of the system.
Conclusion
The adapted vector clock for an ad-hoc distributed system correctly preserves causality,
maintains concurrent event independence, and handles the dynamic nature of processes joining
and leaving the system. The theoretical properties of vector clocks—such as event ordering and
the ability to handle concurrency—are maintained despite the system's dynamic nature.
Therefore, the design of the adapted vector clock is provably correct for use in ad-hoc
distributed systems.