Пример #1
0
    def _apply_committed_log_entries_to_state_machine(self):
        """Apply committed log entries to the state machine."""

        # if there is no new commands to apply then exit from the function
        if self._server_state.no_commands_to_apply():
            return

        for index in six.moves.range(self._server_state.last_applied() + 1,
                                     self._server_state.commit_index() + 1):
            _, _, payload = self._log.entry_at_index(index, decode=True)
            command_type, command_id, params = commands.decode_command(
                payload)
            if command_type == commands.NO_OPERATION:
                continue

            client_id = self._queued_commands.get(command_id, (None, -1))[0]

            try:
                self._state_machine.apply_command(command_type, *params)
            except datatree.FsmException as e:
                self._queue_command_response_if_leader(command_id, client_id,
                                                       e.errno)
            else:
                self._queue_command_response_if_leader(command_id, client_id,
                                                       0)

        self._server_state.update_last_applied()
Пример #2
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]
Пример #3
0
    def _process_response_message(self, socket_dealer, event):
        """Process a response message.

        Decode the message and set the result.

        :param socket_dealer: The zmq.DEALER socket.
        :type socket_dealer: zmq.sugar.socket.Socket
        :param event: The corresponding event, it should only be zmq.POLLIN.
        :type event: int
        """
        response_message = socket_dealer.recv()
        _, response_id, response = commands.decode_command(response_message)
        self._sync_response[response_id]._set_result(response_message)
        del self._sync_response[response_id]
Пример #4
0
    def get(self, timeout=None):
        """Get the result.

        If timeout is None then the method will block indefinitely. If the
        method time out it raises ChillaxTimeoutException.

        :param timeout: The timeout in seconds.
        :type timeout: int
        :return: The result according to the command.
        :rtype: object
        """
        if not self._event.wait(timeout=timeout):
            raise ChillaxTimeoutException()
        return commands.decode_command(self._result)
Пример #5
0
    def _process_response_message(self, socket_dealer, event):
        """Process a response message.

        Decode the message and set the result.

        :param socket_dealer: The zmq.DEALER socket.
        :type socket_dealer: zmq.sugar.socket.Socket
        :param event: The corresponding event, it should only be zmq.POLLIN.
        :type event: int
        """
        response_message = socket_dealer.recv()
        _, response_id, response = commands.decode_command(response_message)
        self._sync_response[response_id]._set_result(response_message)
        del self._sync_response[response_id]
Пример #6
0
    def get(self, timeout=None):
        """Get the result.

        If timeout is None then the method will block indefinitely. If the
        method time out it raises ChillaxTimeoutException.

        :param timeout: The timeout in seconds.
        :type timeout: int
        :return: The result according to the command.
        :rtype: object
        """
        if not self._event.wait(timeout=timeout):
            raise ChillaxTimeoutException()
        return commands.decode_command(self._result)
Пример #7
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))