def update_holdback_queue_casual(self): """ Compare message timestamps to ensure casual ordering. """ while True: new_holdback_queue = [] removed_messages = [] logging.debug("Holdback-queue size: %s" % (len(self.holdback_queue))) # check with vector timestamp, if ordering is correct or some messages are missing for sender, v, message in self.holdback_queue: should_remove = True for item in v: if item == sender: if v[item] != self.my_timestamp[item]: should_remove = False if v[item] > self.my_timestamp[item]: should_remove = False if not should_remove: new_holdback_queue.append((sender, v, message)) else: removed_messages.append((sender, v, message)) # deliver the messages which are removed from holdback queue in this update cycle for sender, v, message in removed_messages: self.my_timestamp[sender] += 1 # update own vector_clock timestamp self.deliver(sender, message) self.holdback_queue = new_holdback_queue if not removed_messages: break
def deliver(self, sender, message): """ Do something with the received message. """ # TODO: save message to database if sender != self.hosts.current_member_ip: self.multicast_delivered(sender, message) else: logging.debug("received own message.") self.multicast_delivered(sender, message)
def announce_service(self, timeout=1): data = "%s:%s" % ("SA", self.own_address) self.socket_sender.send_message(data, type="multicast") logging.debug("SA: service announcement...") time.sleep(timeout) logging.info("SA: service announcement finished.") logging.info("Discovered hosts: %s" % self.hosts.sorted_ring)
def multicast_delivered(self, sender, message): """Callback function of ReliableSocketWorker on new message delivered""" parts = message.split(":") if parts[0] == "AA" and self.election.isLeader: data_set = self.database.get_range(start=-20) for msg in data_set: self.reliable_socket.unicast_send(sender, msg) else: logging.debug('Delivered "%s" from %s' % (message[:15], sender)) self.database.insert(message)
def message_queue_handler(self, running): """ Thread that actually sends out messages when send time <= current_time. """ # TODO: if we have removed randomness in sending messages, can we simplify this? while running: (send_time, message, ip, port, msg_id) = self.queue.get(block=True) if send_time <= time.time(): logging.debug("Send #%s to %s" % (msg_id, ip)) self.sock.sendto(message, (ip, port)) else: self.queue.put((send_time, message, ip, port, msg_id)) time.sleep(0.01)
def insert(self, item): existing = False for entry in self.database: item_parts = item.split(":") parts = entry.split(":") if item_parts[1] == parts[1]: existing = True break if existing is True: self.update(item) else: self.database.append(item) logging.debug("Inserted %s. DB Size: %s" % (item, len(self.database)))
def __init__(self, election, reliable_socket, database): super(ContentProvider, self).__init__() self.election = election self.database = database self.server = WebsocketServer(config["content_websocket_port"], host="0.0.0.0") self.server.set_fn_new_client(self.new_client) self.server.set_fn_client_left(self.client_left) self.server.set_fn_message_received(self.message_received) self.quote_id = uuid.uuid4() # need reliable_socket for data replication self.reliable_socket = reliable_socket self.reliable_socket.set_fn_delivered(self.multicast_delivered) logging.debug("Starting ContentProvider") self._ask_for_initial_database() self.last_update = time.time()
def unicast_receive(self): """ Receive UDP messages from other chat processes and store them in the holdback queue. Returns True if new message was received. """ data, _ = self.sock.recvfrom(self.message_max_size) [sender, message_id, is_ack, message_timestamp, message] = self.unpack_message( data ) logging.debug("Received #%s from %s" % (message_id, sender)) # add sender to timestamps if not yet if sender not in self.my_timestamp: self.my_timestamp[sender] = message_timestamp[sender] # check if hosts in hold-back queue are up-to-date with ring new_timestamp = {} for host in self.my_timestamp: if host in self.hosts.members: new_timestamp[host] = self.my_timestamp[host] self.my_timestamp = new_timestamp logging.debug("Vector Clock: %s" % self.my_timestamp) # check if ack message type if is_ack: # save that ack was send self.has_acknowledged[(sender, message_id)] = True else: # if normal message, send acknowledgement to the sender self.unicast_send(sender, "ACK", msg_id=message_id, is_ack=True) # check if message was send more than once if (sender, message_id) not in self.has_received: self.has_received[(sender, message_id)] = True self.holdback_queue.append((sender, message_timestamp, message)) self.update_holdback_queue_casual() return True return False
def __init__(self, current_member_ip): logging.debug("Ring initialized") self.current_member_ip = current_member_ip self.add_host(self.current_member_ip)
def add_client(self, host): if host not in self.clients: self.clients.append(host) else: logging.debug("Client %s was already added" % host)
def remove_host(self, host): if host in self.members: self.members.remove(host) self.sorted_ring = self._form_ring(self.members) else: logging.debug("Host %s was already removed" % host)
def add_host(self, host): if host not in self.members: self.members.append(host) self.sorted_ring = self._form_ring(self.members) else: logging.debug("Host %s was already discovered" % host)