From c3a17c7ccfda53c59368e41066f1dcb61d4be315 Mon Sep 17 00:00:00 2001
From: Michael Zanger <michaelzanger@Michaels-MacBook-Air.fritz.box>
Date: Tue, 16 Jan 2024 16:41:56 +0100
Subject: [PATCH] fix update cache

---
 server.py | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 84 insertions(+), 7 deletions(-)

diff --git a/server.py b/server.py
index 51042d9..d129148 100644
--- a/server.py
+++ b/server.py
@@ -6,6 +6,7 @@ import time
 import ipaddress
 import netifaces as ni
 import platform
+import uuid
 
 client_broadcast_listener_port = 49153
 server_broadcast_listener_port = 49154
@@ -15,10 +16,16 @@ server_heartbeat_tcp_listener_port = 49152
 client_receive_chat_tcp_port = 50001
 client_forward_message_multicast_port = 51000
 
+leader_election_port = 49155
+
 multicast_group_ip = '224.0.1.1'
 
 last_heartbeat_timestamp = None
 
+# create election sockets
+ring_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ring_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+
 class Server(multiprocessing.Process):
     client_cache_key_offset = 0
     local_servers_cache = dict()
@@ -38,6 +45,10 @@ class Server(multiprocessing.Process):
         self.local_servers_cache = server_cache
         self.local_clients_cache = clients_cache
         self.last_heartbeat_timestamp = last_heartbeat_timestamp
+        ring_socket.bind((self.server_address, leader_election_port))
+        self.server_uuid = self.generate_server_uuid()
+        print("My UUID: ", self.server_uuid)
+        self.participant = True
 
     @staticmethod
     def get_local_ip_address():
@@ -104,6 +115,10 @@ class Server(multiprocessing.Process):
             return "macOS"
         else:
             return "Unknown"
+        
+    @staticmethod
+    def generate_server_uuid():
+        return str(uuid.uuid4())
 
     def run(self):
         print(self.server_id+": "+"Up and running")
@@ -203,7 +218,8 @@ class Server(multiprocessing.Process):
                 if current_time - self.last_heartbeat_timestamp >= timeout_duration:
                     print(f"No heartbeats received for {timeout_duration} seconds. Initiating LCR...")
                     # Call a function to initiate the LCR algorithm here
-                    self.initiate_lcr_algorithm()
+                    print(self.local_servers_cache)
+                    self.start_leader_election(self.server_uuid, self.local_servers_cache)
     
     # find highest server ID in cache
     def get_last_server_id(self):      
@@ -393,13 +409,15 @@ class Server(multiprocessing.Process):
         separator = "_"
 
         MSG = servers_cache_as_string + separator.encode('utf-8') + clients_cache_as_string
-
         broadcast_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
         broadcast_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
+        broadcast_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+        time.sleep(3)
 
         if self.os == "macOS":
             broadcast_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
             broadcast_socket.sendto(MSG, (BROADCAST_ADDRESS, PORT))
+            print("broadcast sent to", BROADCAST_ADDRESS, PORT, "with message", MSG)
         else:
             broadcast_socket.sendto(MSG, (BROADCAST_ADDRESS, PORT))
         broadcast_socket.close()
@@ -408,10 +426,11 @@ class Server(multiprocessing.Process):
         BROADCAST_ADDRESS = self.get_broadcast_address()
         BROADCAST_PORT = 5980
         print("listen for cache update")
+
         # Local host information
-        MY_HOST = socket.gethostname()
-        MY_IP = socket.gethostbyname(MY_HOST)
-        print("listen for cache update")
+        # MY_HOST = socket.gethostname()
+        # MY_IP = socket.gethostbyname(MY_HOST)
+
         # Create a UDP socket
         listen_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
         # Set the socket to broadcast and enable reusing addresses
@@ -419,7 +438,6 @@ class Server(multiprocessing.Process):
         listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
 
         if self.os == "macOS":
-            print("listening for cache updates")
             listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
             listen_socket.bind((BROADCAST_ADDRESS, BROADCAST_PORT))
         else:
@@ -496,4 +514,63 @@ class Server(multiprocessing.Process):
 
             multicast_socket.close()
 
-        
\ No newline at end of file
+    def start_leader_election(self, server, neighbor_dict):
+        self.send_election_message(server, neighbor_dict['server_address'])
+        self.set_participant(True)
+    
+    def send_election_message(server, neighbor_address):
+        election_message = {"mid": server, "isLeader": False}
+        ring_socket.sendto(json.dumps(election_message).encode(), neighbor_address)
+
+    def get_neighbour(servers, current_server, direction):
+        if not servers:
+            return None
+
+        server_ids = list(servers.keys())
+        current_index = server_ids.index(current_server)
+
+        if direction == 'left':
+            neighbor_index = (current_index - 1) % len(server_ids)
+        elif direction == 'right':
+            neighbor_index = (current_index + 1) % len(server_ids)
+        else:
+            return None
+
+        neighbor_server_id = server_ids[neighbor_index]
+        neighbor_server_info = servers[neighbor_server_id]
+
+        return {'server_id': neighbor_server_id, 'server_address': neighbor_server_info}
+
+    def set_participant(p):
+        global participant
+        participant = p
+    
+    def leader_election(self):
+        while True:
+            data, address = ring_socket.recvfrom(4096)
+            if data:
+                election_message = json.loads(data.decode())
+                received_mid = election_message.get('mid')
+                received_mid_id = received_mid['server_uuid']  # Correctly access the server UUID
+                
+                # Compare received_mid_id with self.server_uuid (UUID comparison)
+                if received_mid_id > self.server_uuid:
+                    self.send_election_message(self.server_uuid, self.get_neighbour(self.local_servers_cache, self.server_uuid, 'left')['server_address'])
+                elif received_mid_id == self.server_uuid and self.participant:
+                    self.send_election_message(self.server_uuid, self.get_neighbour(self.local_servers_cache, self.server_uuid, 'left')['server_address'])
+                elif received_mid_id < self.server_uuid and not self.participant:
+                    self.send_election_message(self.server_uuid, self.get_neighbour(self.local_servers_cache, self.server_uuid, 'left')['server_address'])
+                
+                # Determine participant status and leader
+                self.participant = True  # You may adjust this logic
+                if election_message['isLeader']:
+                    self.set_leader(election_message['mid'])
+                    if self.server_uuid != received_mid_id:
+                        self.set_is_leader(False)
+                        self.send_election_message(self.server_uuid, self.get_neighbour(self.local_servers_cache, self.server_uuid, 'left')['server_address'])
+                    else:
+                        self.set_is_leader(True)
+                    self.participant = False  # You may set this as not a participant
+
+
+
-- 
GitLab