예제 #1
0
def test_message_without_retry_dead_letter(settings, mocker, caplog):
    settings.CQRS['replica']['CQRS_MAX_RETRIES'] = 1
    produce_message = mocker.patch(
        'dj_cqrs.transport.rabbit_mq.RabbitMQTransport._produce_message', )

    channel = mocker.MagicMock()
    payload = TransportPayload(
        SignalType.SAVE,
        'basic',
        {'id': 1},
        1,
        correlation_id='abc',
        retries=2,
    )
    delay_queue = DelayQueue()

    PublicRabbitMQTransport.fail_message(channel, 1, payload, None,
                                         delay_queue)

    assert delay_queue.qsize() == 0
    assert channel.basic_nack.call_count == 1

    assert produce_message.call_count == 1

    produce_payload = produce_message.call_args[0][2]
    assert produce_payload is payload
    assert getattr(produce_message, 'is_dead_letter', False)

    assert 'CQRS is failed: pk = 1 (basic), correlation_id = abc, retries = 2.' in caplog.text
    assert (
        'CQRS is added to dead letter queue: pk = 1 (basic), correlation_id = abc'
        in caplog.text)
예제 #2
0
def test_fail_message_with_retry(mocker):
    payload = TransportPayload(SignalType.SAVE, 'basic', {'id': 1}, 1)
    delay_queue = DelayQueue()

    PublicRabbitMQTransport.fail_message(mocker.MagicMock(), 100, payload,
                                         None, delay_queue)

    assert delay_queue.qsize() == 1

    delay_message = delay_queue.get()
    assert delay_message.delivery_tag == 100
    assert delay_message.payload is payload
예제 #3
0
    def consume(cls):
        consumer_rabbit_settings = cls._get_consumer_settings()
        common_rabbit_settings = cls._get_common_settings()

        while True:
            connection = None
            try:
                delay_queue = DelayQueue(max_size=get_delay_queue_max_size())
                connection, channel, consumer_generator = cls._get_consumer_rmq_objects(
                    *(common_rabbit_settings + consumer_rabbit_settings), )

                for method_frame, properties, body in consumer_generator:
                    if method_frame is not None:
                        cls._consume_message(
                            channel,
                            method_frame,
                            properties,
                            body,
                            delay_queue,
                        )
                    cls._process_delay_messages(channel, delay_queue)
            except (exceptions.AMQPError, exceptions.ChannelError,
                    exceptions.ReentrancyError, gaierror):
                logger.error('AMQP connection error. Reconnecting...',
                             exc_info=True)
                time.sleep(cls.CONSUMER_RETRY_TIMEOUT)
            finally:
                if connection and not connection.is_closed:
                    connection.close()
예제 #4
0
def test_delay_queue_get_ready(mocker):
    fake_put_now = datetime(2020, 1, 1, second=0, tzinfo=timezone.utc)
    mocker.patch('django.utils.timezone.now', return_value=fake_put_now)

    delay_queue = DelayQueue()
    delay_messages = []
    for delay in (1, 0, 3600, 2):
        eta = fake_put_now + timedelta(seconds=delay)
        delay_message = DelayMessage(None, None, eta)
        delay_queue.put(delay_message)
        delay_messages.append(delay_message)
    mocker.stopall()

    fake_get_ready_now = datetime(2020, 1, 1, second=3, tzinfo=timezone.utc)
    mocker.patch('django.utils.timezone.now', return_value=fake_get_ready_now)

    ready_messages = list(delay_queue.get_ready())

    assert len(ready_messages) == 3

    sorted_expected = sorted(delay_messages, key=lambda x: x.eta)
    expected_not_ready = sorted_expected.pop()
    for expected, result in zip(sorted_expected, ready_messages):
        assert expected is result

    assert delay_queue.qsize() == 1
    result_message = delay_queue.get()
    assert result_message is expected_not_ready
예제 #5
0
def test_fail_message_invalid_model(mocker, caplog):
    nack = mocker.patch(
        'dj_cqrs.transport.rabbit_mq.RabbitMQTransport._nack', )
    payload = TransportPayload(SignalType.SAVE, 'not_existing', {'id': 1}, 1)
    delay_queue = DelayQueue()

    delivery_tag = 101
    PublicRabbitMQTransport.fail_message(
        mocker.MagicMock(),
        delivery_tag,
        payload,
        None,
        delay_queue,
    )

    assert delay_queue.qsize() == 0
    assert nack.call_count == 1
    assert nack.call_args[0][1] == delivery_tag

    assert 'Model for cqrs_id not_existing is not found.' in caplog.text
예제 #6
0
def test_delay_queue_put_full():
    eta = datetime(2020, 1, 1, second=0, tzinfo=timezone.utc)
    delay_queue = DelayQueue(max_size=1)

    delay_queue.put(
        DelayMessage(1, None, eta),
    )
    with pytest.raises(Full):
        delay_queue.put(
            DelayMessage(2, None, eta),
        )

    assert delay_queue.qsize() == 1
    assert delay_queue.get().delivery_tag == 1
예제 #7
0
def test_process_delay_messages(mocker, caplog):
    channel = mocker.MagicMock()
    produce = mocker.patch(
        'dj_cqrs.transport.rabbit_mq.RabbitMQTransport.produce')

    payload = TransportPayload(SignalType.SAVE, 'CQRS_ID', {'id': 1}, 1)
    delay_queue = DelayQueue()
    delay_queue.put(
        DelayMessage(delivery_tag=1,
                     payload=payload,
                     eta=datetime.now(tz=timezone.utc)), )

    PublicRabbitMQTransport.process_delay_messages(channel, delay_queue)

    assert delay_queue.qsize() == 0
    assert channel.basic_nack.call_count == 1
    assert produce.call_count == 1

    produce_payload = produce.call_args[0][0]
    assert produce_payload is payload
    assert produce_payload.retries == 1
    assert getattr(produce_payload, 'is_requeue', False)

    assert 'CQRS is requeued: pk = 1 (CQRS_ID)' in caplog.text
예제 #8
0
    def _get_delay_queue(cls):
        replica_settings = settings.CQRS.get('replica', {})
        max_size = DEFAULT_DELAY_QUEUE_MAX_SIZE
        if 'delay_queue_max_size' in replica_settings:
            max_size = replica_settings['delay_queue_max_size']

        if max_size is not None and max_size <= 0:
            logger.warning(
                "Settings delay_queue_max_size=%s is invalid, using default %s.",
                max_size,
                DEFAULT_DELAY_QUEUE_MAX_SIZE,
            )
            max_size = DEFAULT_DELAY_QUEUE_MAX_SIZE

        return DelayQueue(max_size)
예제 #9
0
def test_delay_queue_put_same_eta():
    eta = datetime(2020, 1, 1, second=0, tzinfo=timezone.utc)
    delay_messages = [DelayMessage(delivery_tag, None, eta) for delivery_tag in range(10)]

    delay_queue = DelayQueue()
    for delay_message in delay_messages:
        delay_queue.put(delay_message)

    assert delay_queue.qsize() == 10
    assert delay_queue.get()
예제 #10
0
def test_delay_queue_put():
    fake_now = datetime(2020, 1, 1, second=0, tzinfo=timezone.utc)
    delay_message = DelayMessage(1, {'test': 'data'}, fake_now)

    delay_queue = DelayQueue()
    delay_queue.put(delay_message)

    assert delay_queue.qsize() == 1

    result_message = delay_queue.get()
    assert result_message is delay_message
예제 #11
0
def test_delay_message_with_requeue(mocker, caplog):
    channel = mocker.MagicMock()
    requeue_message = mocker.patch(
        'dj_cqrs.transport.rabbit_mq.RabbitMQTransport._requeue_message', )

    delay_messages = []
    for delay in (2, 1, 3):
        payload = TransportPayload(SignalType.SAVE, 'CQRS_ID', {'id': delay},
                                   delay)
        eta = datetime.now(tz=timezone.utc) + timedelta(hours=delay)
        delay_message = DelayMessage(delivery_tag=delay,
                                     payload=payload,
                                     eta=eta)
        delay_messages.append(delay_message)

    delay_queue = DelayQueue(max_size=3)
    for delay_message in delay_messages:
        delay_queue.put(delay_message)

    exceeding_delay = 0
    exceeding_payload = TransportPayload(SignalType.SAVE, 'CQRS_ID', {'id': 4},
                                         4)
    PublicRabbitMQTransport.delay_message(
        channel,
        4,
        exceeding_payload,
        exceeding_delay,
        delay_queue,
    )

    assert delay_queue.qsize() == 3
    assert delay_queue.get().payload is exceeding_payload
    assert (
        'CQRS is delayed: pk = 4 (CQRS_ID), correlation_id = None, delay = 0 sec'
        in caplog.text)

    assert requeue_message.call_count == 1

    requeue_payload = requeue_message.call_args[0][2]
    min_eta_delay_message = sorted(delay_messages, key=lambda x: x.eta)[0]
    assert requeue_payload is min_eta_delay_message.payload
예제 #12
0
def test_delay_queue_invalid_max_size():
    with pytest.raises(AssertionError) as e:
        DelayQueue(max_size=0)

    assert e.value.args[0] == "Delay queue max_size should be positive integer."