Example #1
0
 def _handle_conn(self, conn) -> None:
     # Can only handle one connection at a time
     logging.info('Servicing incoming connection')
     self.sock = conn
     while True:
         try:
             (typ, payload) = self._recv_packet()
         except IOError as e:
             logging.info('Connection closed')
             break
         if typ in PKT_CHANGE_TYPES:
             change = change_from_packet(typ, payload)
             if typ == PacketType.CHANGE:
                 logging.debug('Received CHANGE {}'.format(change.version))
             else:
                 logging.info('Received SNAPSHOT {}'.format(change.version))
             self.backend.add_change(change)
             self._send_packet(PacketType.ACK,
                               struct.pack("!I", self.backend.version))
         elif typ == PacketType.REWIND:
             logging.info('Received REWIND')
             to_version, = struct.unpack('!I', payload)
             if to_version != self.backend.prev_version:
                 logging.info(
                     'Cannot rewind to version {}'.format(to_version))
                 self._send_packet(PacketType.NACK,
                                   struct.pack("!I", self.backend.version))
             else:
                 self.backend.rewind()
                 self._send_packet(PacketType.ACK,
                                   struct.pack("!I", self.backend.version))
         elif typ == PacketType.REQ_METADATA:
             logging.debug('Received REQ_METADATA')
             blob = struct.pack("!IIIQ", 0x01, self.backend.version,
                                self.backend.prev_version,
                                self.backend.version_count)
             self._send_packet(PacketType.METADATA, blob)
         elif typ == PacketType.RESTORE:
             logging.info('Received RESTORE')
             for change in self.backend.stream_changes():
                 (typ, payload) = packet_from_change(change)
                 self._send_packet(typ, payload)
             self._send_packet(PacketType.DONE, b'')
         elif typ == PacketType.COMPACT:
             logging.info('Received COMPACT')
             stats = self.backend.compact()
             self._send_packet(PacketType.COMPACT_RES,
                               json.dumps(stats).encode())
         elif typ == PacketType.ACK:
             logging.debug('Received ACK')
         elif typ == PacketType.NACK:
             logging.debug('Received NACK')
         elif typ == PacketType.METADATA:
             logging.debug('Received METADATA')
         elif typ == PacketType.COMPACT_RES:
             logging.debug('Received COMPACT_RES')
         else:
             raise Exception(
                 'Unknown or unexpected packet type {}'.format(typ))
     self.conn = None
Example #2
0
 def add_change(self, entry: Change) -> bool:
     typ, payload = packet_from_change(entry)
     self._send_packet(typ, payload)
     # Wait for change to be acknowledged before continuing.
     (typ, _) = self._recv_packet()
     assert(typ == PacketType.ACK)
     return True
Example #3
0
    def add_change(self, entry: Change) -> bool:
        typ, payload = packet_from_change(entry)

        base_version = self.version
        retry = 0
        retry_delay = RECONNECT_DELAY
        need_connect = False
        while True:  # Retry loop
            try:
                if need_connect:
                    self.connect()
                    # Request metadata, to know where we stand
                    self._request_metadata()
                    if self.version == entry.version:
                        # If the current version at the server side matches the version of the
                        # entry, the packet was succesfully sent and processed and the error
                        # happened afterward. Nothing left to do.
                        return True
                    elif base_version == self.version:
                        # The other acceptable option is that the current version still matches
                        # that on the server side. Then we retry.
                        pass
                    else:
                        raise Exception(
                            'Unexpected backup version {} after reconnect'.
                            format(self.version))

                self._send_packet(typ, payload)
                # Wait for change to be acknowledged before continuing.
                (typ, _) = self._recv_packet()
                assert (typ == PacketType.ACK)
            except (BrokenPipeError, OSError):
                pass
            else:
                break

            if retry == RECONNECT_TRIES:
                logging.error(
                    'Connection was lost while sending change (giving up after {} retries)'
                    .format(retry))
                raise IOError('Connection was lost while sending change')

            retry += 1
            logging.warning(
                'Connection was lost while sending change (retry {} of {}, will try again after {} seconds)'
                .format(retry, RECONNECT_TRIES, retry_delay))
            time.sleep(retry_delay)
            retry_delay *= RECONNECT_DELAY_BACKOFF
            need_connect = True

        self.prev_version = self.version
        self.version = entry.version
        return True