Example #1
0
def send_to_worker(worker, func_name, request, args=()):
    """
    Send command to other worker or execute command locally.

    @param worker: int
    @param func_name: str
    @param request: dict - defined in "on_request"
    @param args: tuple(str)
    """

    if log.level == logging.DEBUG or config['grep']:
        verbose('w{} > w{}: {}{} for request={}'.format(
            state.worker, worker, func_name, args, request))

    if worker == state.worker:
        execute(func_name, request, args)

    else:
        wall = gbn('send_to_worker.other')

        # "command protocol" encodes 40x faster than default "pickle.dumps" and produces 9x smaller result:
        command = '\t'.join((func_name, request['id'],
                             request.get('client', '-'),
                             str(request.get('worker', -1)),
                             str(int(request.get('confirm', False)))) + args)

        if len(command) >= config['warn_command_bytes']:
            crit('WARNING! Too big: "{}" == {} >= {} bytes'.format(
                command, len(command), config['warn_command_bytes']))

        state.commands_to_workers[worker].put(command)
        state.commands_put += 1

        gbn(wall=wall)
Example #2
0
def _delete_queue(request, queue):
    """
    Delete queue command

    @param request: dict - defined in "on_request"
    @param queue: str
    """

    if log.level == logging.DEBUG or config['grep']:
        verbose('w{}: deleting queue {}'.format(state.worker, queue))

    confirm = request['confirm']
    request['confirm'] = False  # To avoid double confirm.

    rebind(request, queue)  # Unbind all by default.

    for consumer_id, queue_of_consumer in state.queues_by_consumer_ids.items():
        if queue_of_consumer == queue:
            _delete_consumer(request, consumer_id)

    state.queues.pop(queue, None)
    state.queues_to_delete_when_unused.pop(queue, None)

    queue_used = state.queues_used.pop(queue, None)
    if queue_used:
        queue_used.set()  # Cancel "_wait_used_or_delete_queue" greenlet.

    if log.level == logging.DEBUG or config['grep']:
        verbose('w{}: deleted queue {}'.format(state.worker, queue))

    if confirm:
        respond(request)
Example #3
0
def delete_consumers(client):
    """
    Delete consumers on disconnect of client.

    @param client: str
    """
    wall = gbn('delete_consumers')
    for consumer_id in list(state.consumer_ids_by_clients.get(client, ())):
        # get() is used instead of pop() because
        # "_delete_consumer" will discard "consumer_id" from "consumer_ids" to stop "_consume_loop",
        # and once client has no consumers - it will pop().
        # list(set()) is used to avoid "Set changed size during iteration".

        if log.level == logging.DEBUG or config['grep']:
            verbose(
                'w{}: deleting consumer {} on disconnect of client {}'.format(
                    state.worker, consumer_id, client))

        request = dict(id='disconnect',
                       client=client,
                       worker=state.worker,
                       confirm=False)
        _delete_consumer(request, consumer_id)

    gbn(wall=wall)
Example #4
0
def on_request(request):
    """
    Handler of request from client.

    @param request: dict(
        client: str,
        worker: int,
        body: str,
    )

    "action()" gets "request" without "body" but with (
        id: str,
        action: str,
        data: str - should be parsed inside "action()",
        confirm: bool,
    )

    Command in another worker gets "request" without (
        action: str - unused,
        data: str - unused and may be very big
    )
    """
    wall = gbn('on_request')
    request['id'] = None
    try:
        body = request['body'].rstrip()
        request['id'], request['action'], request['data'] = body.split(' ', 2)
        del request[
            'body']  # Less data to pass between workers. On error "request" with "id, action, data" will be logged.

        if log.level == logging.DEBUG or config['grep']:
            verbose('w{}: {}#{} > {} {}'.format(state.worker,
                                                request['client'],
                                                request['id'],
                                                request['action'],
                                                request['data']))

        request['confirm'] = request['data'].startswith('--confirm ')
        if request['confirm']:
            request['data'] = request['data'][10:]

        action = state.actions[request['action']]
        wall = gbn(request['action'], wall=wall)
        action(request)
        gbn(wall=wall)

    except Exception:
        request['error_id'] = dtid(config['id_length'])
        crit(also=request)
        try:
            respond(request)
        except Exception:
            crit(also=request)
        gbn(wall=wall)
Example #5
0
def responder(client):
    """
    Sends queued responses to socket of client.
    See also "mqks.server.lib.workers.respond()" that enqueues response to "state.responses_by_clients[client]".

    @param client: str
    """

    response = None
    try:
        responses = state.responses_by_clients.get(client)
        sock = state.socks_by_clients.get(client)
        if not responses or not sock:
            return

        while client in state.responses_by_clients:
            try:
                response = responses.get(timeout=config['block_seconds'])
            except Empty:
                continue

            wall = gbn('responder')
            try:
                request, data = response
                error_id = request.get('error_id')
                response = '{} {}'.format('error' if error_id else 'ok',
                                          error_id or data)
                if log.level == logging.DEBUG or config['grep']:
                    verbose('w{}: {}#{} < {}'.format(state.worker, client,
                                                     request['id'], response))
                response = '{} {}\n'.format(request['id'], response)

            except Exception:
                gbn(wall=wall)
                crit(also=dict(response=response))
                continue

            try:
                sock.sendall(response)
                # Disconnect on socket error.
            finally:
                gbn(wall=wall)

            time.sleep(0)

    except Exception as e:
        if not is_disconnect(e):
            crit(also=dict(client=client, response=response))
Example #6
0
def reject(request):
    """
    Reject action

    @param request: dict - defined in "on_request" with (
        data: str - "{consumer_id} {msg_id}",
        ...
    )
    """
    consumer_id, msg_id = request['data'].split(' ', 1)
    queue = state.queues_by_consumer_ids.get(consumer_id)
    if queue:
        _reject(request, queue, consumer_id, msg_id)
    elif log.level == logging.DEBUG or config['grep']:
        verbose('w{}: found no queue for request={}'.format(
            state.worker, request))
Example #7
0
def execute(func_name, request, args):
    """
    Execute the command and handle errors.

    @param func_name: str
    @param request: dict - defined in "on_request"
    @param args: sequence(str)
    """
    try:
        wall = gbn(func_name)

        if log.level == logging.DEBUG or config['grep']:
            verbose('w{} < {}{} for request={}'.format(state.worker, func_name,
                                                       args, request))

        func = state.funcs[func_name]
        func(request, *args)
        gbn(wall=wall)

    except Exception:
        on_error((func_name, request, args), request)
Example #8
0
def ack(request):
    """
    Ack action

    @param request: dict - defined in "on_request" with (
        data: str - "{consumer_id} {msg_id}" or "{consumer_id} --all",
        ...
    )
    """
    consumer_id, msg_id = request['data'].split(' ', 1)

    queue = state.queues_by_consumer_ids.get(consumer_id)
    if not queue:
        if log.level == logging.DEBUG or config['grep']:
            verbose('w{}: found no queue for request={}'.format(state.worker, request))
        return

    if msg_id == '--all':
        state.messages_by_consumer_ids.pop(consumer_id, {}).clear()
    else:
        state.messages_by_consumer_ids.get(consumer_id, {}).pop(msg_id, None)

    if request['confirm']:
        respond(request)