Task submission for WEC systems recruitment
Deepak C Nayak
Sophomore, AI
The .py file is self-sufficient and has no dependencies that need to be installed. Run the Python file (preferably on an IDE) and follow these steps:
- When prompted to enter the number of clients, enter any number of your choice. The code will create those many clients to be part of the distributed system.
- When prompted to enter the TOTAL number of operations, again enter any number BIGGER than the number of clients to ensure smooth running of the program.
This section provides an overview of the program's functionality as a whole. We will delve into the finer details in subsequent sections.
Every client
is an instance of the CLient
class, each client
has a corresponding file and a thread, and other attributes.
A client
thread will make changes into the client's corresponding file through the function run_client
.
The run_client
function will account for real world latency by putting the operating thread to sleep for a random time interval.
Then the function will randomly choose one of write, read or update operations, that is to be performed on the client's file and makes appropriate function calls.
All the 3 functions (read/write/update) do the following:
- Update the client's vector clock.
- Log the operation along with the client ID, Network timestamp, and the newly updated vector clock to the
Client_log.txt
file. - Once the logging is done, perform the operation on the client's file.
- After performing the operation, a snapshot of the file is taken by invoking
file_snapshot
function. - Synchronise all other files in the system to be up-to-date with the last operation by invoking
update_files
function.
All the steps above are performed by the thread while having acquired a lock at step 1 and releasing it after step 5. This lock is the reason why we do not need to explicitly have a mechanism to rule out causal anomalies, the lock makes sure that the events are casually consistent.
NOTE: THE CONTENT BELOW WAS ADDED AFTER THE DEADLINE, FOR MORE CLARITY. THE CODE HASN'T BEEN MODIFIED IN ANY WAY.
Here we will take a deeper look at how the code works and is designed to maintain causal consistency
Let us imagine we have 3 Clients - client1
, client2
and client3
Let the operations be as follows:
client1
updates their file - U
client2
writes in their file - W
client3
reads their file - R
and let the order be (for simplicity) W
, U
, R
Here is the overall process:
We will refer to the lock as L
and the threads as T1
, T2
, T3
corresponding to the clients in the same order.
T1
,T2
,T3
are startedT1
invokesrun_client
T1
will be put to sleep for a random time interval to simulate real-world latency- The same happens with
T2
andT3
parallelly. - Whichever thread wakes up first gets to choose an operation - here we are assuming
T2
woke up first and choseW
T2
invokesclient_write
client_write
invokesupdate_vector_clock
- Once the thread has reached
update_vector_clock
, it acquiresL
, a static attribute of classCLient
.
- This means no other thread now can enter
update vector clock
. T1
andT3
(in that order) will also invokeclient_update
andclient_read
respectively and those in turn will invokeupdate_vector_clock
- But since
T2
has acquired the lock,T1
andT3
will be queued until theL
has been released.
update_vector_clock
updates the vector clock dictionary (again a static variable) and calls thelog
functionlog
logs the operation inClient_log.txt
with all necessary data like the client performing it, time stamp, network time, the kind of operation, and the operation ID.- Now after logging the operation, the thread
T2
returns back toclient_write
. The function creates the file (since it doesn't exist yet) and writes data into it. client_write
will now invokefile_snapshot
to save the state of the client's file at this point in time.file_snapshot
will make a copy of the client's file at that point in time, name it asEVENT_ID-CLIENT_ID-OPERATION_TYPE.txt
and save it in a separate directory.Client_log.txt
will be updated to have the path of the corresponding file snapshot.- After this,
client_write
invokesupdate_files
. This is to synchronize all the other client files in the system to be up-to-date with the changes the client made to their file. update_files
scans for all the client files other than the client performing the operation, and updates them with the content from the operating client's file.- And now,
T2
releasesL
, allowing other threads in the queue to perform their activities.
After the lock has been released, the next thread in the queue (T1
) will acquire it and repeat the process above (steps 7 - 17) with its respective operation.
This process goes on until a certain number of operations (in total) are completed. This number is specified at the beginning of the program by the user.