Exemple #1
0
    def _send_write_responses(self, first_non_ack_command_index):
        """Send acknowledgments for write requests.

        Once the leader committed commands it sends the acknowledgments
        to the corresponding client.

        :param first_non_ack_command_index: the first command index which the
        server didn't send an acknowledgment.
        :type first_non_ack_command_index: int
        """
        for index in six.moves.range(first_non_ack_command_index,
                                     self._server_state.commit_index() + 1):
            command = self._log.entry_at_index(index, decode=True)[2]
            command_type, command_id, payload = commands.decode_command(
                command)

            if command_type == commands.NO_OPERATION:
                self._queued_commands.pop(command_id, None)
                continue

            client_id, status = self._queued_commands.get(command_id,
                                                          (None, -1))
            if client_id:
                response = commands.build_response(command_type, command_id,
                                                   status)
                self._socket_for_commands.send_multipart((client_id, response))
                del self._queued_commands[command_id]
Exemple #2
0
    def _process_command_message(self, socket, event):
        """Processes a command from a client.

        In case of a read command the server respond immediately to the client
        otherwise it just add the command in the log so that it will be
        replicated.

        :param socket: The zmq.ROUTER socket.
        :type socket: zmq.sugar.socket.Socket
        :param event: The corresponding event, it should only be zmq.POLLIN.
        :type event: int
        """

        assert event == zmq.POLLIN
        zmq_message = socket.recv_multipart()
        client_identifier, command = zmq_message[0], zmq_message[1]

        command_type, command_id, payload = commands.decode_command(command)

        # If it is a read command then just send immediately the result.
        if commands.is_read_command(command_type):
            try:
                data = self._state_machine.apply_command(command_type,
                                                         *payload)
            except datatree.FsmException as e:
                response = commands.build_response(command_type, command_id,
                                                   e.errno)
            else:
                response = commands.build_response(command_type, command_id, 0,
                                                   data)
            socket.send_multipart((client_identifier, response))
        else:
            # If it is a write command then:
            #     1. add the command to the commands queue
            #     2. append it to the log, it will be piggybacked
            #        with the next heartbeat among the cluster
            self._queued_commands[command_id] = (client_identifier, -1)
            self._log.append_entry(self._server_state.term(), command)
            if self.is_standalone():
                # If it's a standalone server then we can directly commit
                # the command.
                new_commit_index = self._server_state.commit_index() + 1
                self._server_state.update_commit_index(new_commit_index)
                self._apply_committed_log_entries_to_state_machine()
                self._send_write_responses(
                    max(1, self._server_state.commit_index() - 1))