Skip to content
Snippets Groups Projects
Commit f9ef13a2 authored by Dixon Tirtayadi's avatar Dixon Tirtayadi
Browse files

Finished paxos

parent 5106307d
No related branches found
No related tags found
No related merge requests found
......@@ -32,9 +32,23 @@ Write-up
Config file to indicate the addresses of the paxos group: config.json
Spawn the paxos group based on the config file using:
python3 main.py
Note: Can you use command kill [node] or run [node] to kill and bring back up paxos nodes. Node starts from 0
e.g. python3 main.py
kill 0
kill 1
run 0
run 1
Also, for every command done, The LockManager will print the state of locks so you can see what is the state of Locking
and check that it is consistent. For example:
"Current Locks: {2: ('0.0.0.0', 36086), 1: ('0.0.0.0', 53889)}" means that there are two locks currently acquired,
Lock on value 2 held by client at ('0.0.0.0', 36086)
Lock on value 1 held by client at ('0.0.0.0', 53889)
Start the client and send commands to a random paxos server based on the config file using:
python3 client.py [client_host] [client_port]
e.g. python3 client.py localhost 0
python3 client.py
e.g. python3 client.py
lock 1
lock 2
unlock 1
......@@ -42,15 +56,10 @@ Write-up
exit
Note that lock can be acquired twice. One client sending lock 1 and lock 1 is ok, and only need single unlock 1 to be unlocked.
Also, for every command done by the server. The LockManager will print the state of locks so you can see what is the state of Locking
and check that it is consistent. For example:
"Current Locks: {2: ('0.0.0.0', 36086), 1: ('0.0.0.0', 53889)}" means that there are two locks currently acquired,
Lock on value 2 held by client at ('0.0.0.0', 36086)
Lock on value 1 held by client at ('0.0.0.0', 53889)
- Any issues or scenarios explicitly not considered
At-most-once is not implemented in server side. It is possible for client to resend command and server executes it twice.
- Any other behavior of the system you feel important to discuss (e.g. quirks, interesting behavior, performance characteristics, etc)
We intentionally left some important print statements (Leader failure is detected, ) so that the tester can also see what is happening.
Extra Implementation:
- Resending message. Use timeout for resending messages that may have been lost to keep Paxos going.
\ No newline at end of file
......@@ -30,7 +30,7 @@ class LockClient():
try:
while input_str != "exit":
lock, val = input_str.split(" ", 2)
paxos_req = PaxosRequest(self.client_addr, LockCommand(lock, val))
paxos_req = PaxosRequest(self.client_addr, LockCommand(lock, val, self.req_num))
data = pickle.dumps(paxos_req)
server_address = random.choice(self.server_addresses)
......@@ -45,6 +45,9 @@ class LockClient():
# receive data back from the server
res = pickle.loads(self.sock.recv(1024))
while res.cmd.req_num != self.req_num:
res = pickle.loads(self.sock.recv(1024))
self.req_num += 1
print(f"Received response: {res}", file=sys.stdout)
......
......@@ -13,7 +13,7 @@ class LockManager:
# a locks it already has, false otherwise
def lock(self, value: int, client: Address) -> bool:
# acquire lock
print(f"Locking {value} for {client}", file=sys.stdout)
# print(f"Locking {value} for {client}", file=sys.stdout)
if value not in self.locks:
self.locks[value] = client
return True
......@@ -27,7 +27,7 @@ class LockManager:
# returns true if lock is unlocked, false otherwise
def unlock(self, value: int, client: Address) -> bool:
print(f"Unlocking {value} for {client}", file=sys.stdout)
# print(f"Unlocking {value} for {client}", file=sys.stdout)
# lock does not exist
if value not in self.locks:
return False
......@@ -41,11 +41,13 @@ class LockManager:
return True
def lockstatus(self):
print(f"\nCurrent Locks: {self.locks}\n", file=sys.stdout)
def execute(self, lock_command: LockCommand, client_address: Address) -> bool:
lock_res = False
if lock_command.op == "lock":
lock_res = self.lock(int(lock_command.value), client_address)
elif lock_command.op == "unlock":
lock_res = self.unlock(int(lock_command.value), client_address)
print(f"Current Locks: {self.locks}", file=sys.stdout)
return lock_res
......@@ -18,7 +18,6 @@ if __name__ == "__main__":
paxos_port = addresses[i + 1]
proc = subprocess.Popen(["python3", "paxos.py", paxos_host, paxos_port] + addresses)
processes.append(proc)
print(processes)
# Needed to sleep forever here until ctrl-c so we go into "finally" and kill processes
input_str = input() # Input would be "lock 2" / "unlock 1", etc or "exit"
while input_str != "exit":
......@@ -27,14 +26,13 @@ if __name__ == "__main__":
if node > len(addresses) / 2:
print("Node does not exists")
elif command == "kill":
print(f"Killing node number {processes[node]}")
print(f"Killing node ({addresses[node*2]}, {addresses[(node*2) + 1]})")
processes[node].kill()
elif command == "run":
print(f"Bringing back up node number {node}")
print(f"Bringing back up node number ({addresses[node*2]}, {addresses[(node*2) + 1]})")
paxos_host = addresses[node*2]
paxos_port = addresses[(node*2) + 1]
processes[node] = subprocess.Popen(["python3", "paxos.py", paxos_host, paxos_port] + addresses)
print(processes)
input_str = input()
finally:
for proc in processes:
......
......@@ -19,17 +19,6 @@ class LeaderHeartbeat(Message):
def __str__(self) -> str:
return f"LeaderHeartbeat({super().__str__()}, {str(self.ballot_num)}, log: {self.log})"
class HeartbeatResponse(Message):
def __init__(self, addr: Address, last_executed_slot: int, highest_ballot_seen: BallotNumber) -> None:
super().__init__(addr)
self.last_executed_slot: int = last_executed_slot
self.highest_ballot_seen: BallotNumber = highest_ballot_seen
def __str__(self) -> str:
return f"HeartbeatResponse({super().__str__()}, last_executed_slot: {str(self.last_executed_slot)}, highest_ballot_seen: {self.highest_ballot_seen})"
class P1A(Message):
def __init__(self, addr: Address, ballot_num: BallotNumber) -> None:
super().__init__(addr)
......@@ -90,20 +79,22 @@ class PaxosRequest(Message):
class PaxosResult(Message):
def __init__(self, addr: Address, value: bool) -> None:
def __init__(self, addr: Address, value: bool, lock_command) -> None:
super().__init__(addr)
self.value: bool = value
self.cmd: LockCommand = lock_command
def __str__(self) -> str:
return f"PaxosResult({super().__str__()}, value: {str(self.value)})"
return f"PaxosResult({super().__str__()}, value: {str(self.value)}, cmd: {str(self.cmd)})"
class LockCommand:
def __init__(self, op, value) -> None:
def __init__(self, op, value, req_num) -> None:
# op is either "lock" or "unlock"
# value is value to lock (int)
self.op = op
self.value = value
self.req_num = req_num
def __str__(self) -> str:
return f"LockCommand(op: {str(self.op)}, value: {str(self.value)})"
This diff is collapsed.
......@@ -16,7 +16,7 @@ class Timer:
return f"timer: {self.__class__.__name__}\n"
class ClientTimer(Timer):
CLIENT_RETRY_MILLIS = 20000
CLIENT_RETRY_MILLIS = 5000
def __init__(self, request: PaxosRequest, req_num: int) -> None:
super().__init__()
......@@ -24,7 +24,7 @@ class ClientTimer(Timer):
self.req_num: int = req_num
class P2ATimer(Timer):
P2A_RETRY_MILLIS = 5000
P2A_RETRY_MILLIS = 500
def __init__(self, p2a: P2A) -> None:
super().__init__()
......@@ -32,14 +32,14 @@ class P2ATimer(Timer):
class HeartBeatTimer(Timer):
HEARTBEAT_RETRY_MILLIS = 1000
HEARTBEAT_RETRY_MILLIS = 200
def __init__(self) -> None:
super().__init__()
class HeartBeatCheckTimer(Timer):
HEARTBEAT_CHECK_RETRY_MILLIS = 5000
HEARTBEAT_CHECK_RETRY_MILLIS = 1000
def __init__(self, ballot_num: BallotNumber) -> None:
super().__init__()
......@@ -47,7 +47,7 @@ class HeartBeatCheckTimer(Timer):
class LeaderElectionTimer(Timer):
LEADER_ELECTION_TIMER = 50
LEADER_ELECTION_TIMER = 500
def __init__(self, p1a: P1A) -> None:
super().__init__()
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment