def test_reduce_sms_provider_priority_adds_rows_to_history_table(
        mocker, restore_provider_details, sample_user):
    mocker.patch('app.dao.provider_details_dao.get_user_by_id',
                 return_value=sample_user)
    mmg = get_provider_details_by_identifier('mmg')
    # need to update these manually to avoid triggering the `onupdate` clause of the updated_at column
    ProviderDetails.query.filter(
        ProviderDetails.notification_type == 'sms').update(
            {'updated_at': datetime.min})

    provider_history_rows = ProviderDetailsHistory.query.filter(
        ProviderDetailsHistory.id == mmg.id).order_by(
            desc(ProviderDetailsHistory.version)).all()

    dao_reduce_sms_provider_priority(mmg.identifier,
                                     time_threshold=timedelta(minutes=10))

    updated_provider_history_rows = ProviderDetailsHistory.query.filter(
        ProviderDetailsHistory.id == mmg.id).order_by(
            desc(ProviderDetailsHistory.version)).all()

    assert len(updated_provider_history_rows) - len(provider_history_rows) == 1
    assert updated_provider_history_rows[0].version - provider_history_rows[
        0].version == 1
    assert updated_provider_history_rows[0].priority == 90
def test_reduce_sms_provider_priority_switches_provider(
    notify_db_session,
    mocker,
    restore_provider_details,
    sample_user,
    starting_priorities,
    expected_priorities,
):
    mocker.patch('app.dao.provider_details_dao.get_user_by_id',
                 return_value=sample_user)
    mmg = get_provider_details_by_identifier('mmg')
    firetext = get_provider_details_by_identifier('firetext')

    mmg.priority = starting_priorities['mmg']
    firetext.priority = starting_priorities['firetext']
    # need to update these manually to avoid triggering the `onupdate` clause of the updated_at column
    ProviderDetails.query.filter(
        ProviderDetails.notification_type == 'sms').update(
            {'updated_at': datetime.min})

    # switch away from mmg. currently both 50/50
    dao_reduce_sms_provider_priority('mmg',
                                     time_threshold=timedelta(minutes=10))

    assert firetext.priority == expected_priorities['firetext']
    assert mmg.priority == expected_priorities['mmg']
    assert mmg.created_by is sample_user
    assert firetext.created_by is sample_user
Exemple #3
0
def test_reduce_sms_provider_priority_adjusts_provider_priorities(
    mocker,
    restore_provider_details,
    notify_user,
    starting_priorities,
    expected_priorities,
):
    mock_adjust = mocker.patch(
        'app.dao.provider_details_dao._adjust_provider_priority')

    mmg = get_provider_details_by_identifier('mmg')
    firetext = get_provider_details_by_identifier('firetext')

    mmg.priority = starting_priorities['mmg']
    firetext.priority = starting_priorities['firetext']
    # need to update these manually to avoid triggering the `onupdate` clause of the updated_at column
    ProviderDetails.query.filter(
        ProviderDetails.notification_type == 'sms').update(
            {'updated_at': datetime.min})

    # switch away from mmg. currently both 50/50
    dao_reduce_sms_provider_priority('mmg',
                                     time_threshold=timedelta(minutes=10))

    mock_adjust.assert_any_call(firetext, expected_priorities['firetext'])
    mock_adjust.assert_any_call(mmg, expected_priorities['mmg'])
Exemple #4
0
def send_sms_to_provider(notification):
    service = notification.service

    if not service.active:
        technical_failure(notification=notification)
        return

    if notification.status == 'created':
        provider = provider_to_use(SMS_TYPE, notification.international)

        template_model = dao_get_template_by_id(notification.template_id,
                                                notification.template_version)

        template = SMSMessageTemplate(
            template_model.__dict__,
            values=notification.personalisation,
            prefix=service.name,
            show_prefix=service.prefix_sms,
        )

        if service.research_mode or notification.key_type == KEY_TYPE_TEST:
            update_notification_to_sending(notification, provider)
            send_sms_response(provider.get_name(), str(notification.id),
                              notification.to)

        else:
            try:
                provider.send_sms(to=validate_and_format_phone_number(
                    notification.to, international=notification.international),
                                  content=str(template),
                                  reference=str(notification.id),
                                  sender=notification.reply_to_text)
            except Exception as e:
                notification.billable_units = template.fragment_count
                dao_update_notification(notification)
                dao_reduce_sms_provider_priority(
                    provider.get_name(), time_threshold=timedelta(minutes=1))
                raise e
            else:
                notification.billable_units = template.fragment_count
                update_notification_to_sending(notification, provider)

        delta_seconds = (datetime.utcnow() -
                         notification.created_at).total_seconds()
        statsd_client.timing("sms.total-time", delta_seconds)

        if notification.key_type == KEY_TYPE_TEST:
            statsd_client.timing("sms.test-key.total-time", delta_seconds)
        else:
            statsd_client.timing("sms.live-key.total-time", delta_seconds)
            if str(service.id) in current_app.config.get(
                    'HIGH_VOLUME_SERVICE'):
                statsd_client.timing("sms.live-key.high-volume.total-time",
                                     delta_seconds)
            else:
                statsd_client.timing("sms.live-key.not-high-volume.total-time",
                                     delta_seconds)
Exemple #5
0
def test_reduce_sms_provider_priority_does_nothing_if_providers_have_recently_changed(
    mocker,
    restore_provider_details,
):
    mock_get_providers = mocker.patch('app.dao.provider_details_dao._get_sms_providers_for_update', return_value=None)
    mock_adjust = mocker.patch('app.dao.provider_details_dao._adjust_provider_priority')

    dao_reduce_sms_provider_priority('firetext', time_threshold=timedelta(minutes=5))

    mock_get_providers.assert_called_once_with(timedelta(minutes=5))
    assert mock_adjust.called is False
def switch_current_sms_provider_on_slow_delivery():
    """
    Reduce provider's priority if at least 30% of notifications took more than four minutes to be delivered
    in the last ten minutes. If both providers are slow, don't do anything. If we changed the providers in the
    last ten minutes, then don't update them again either.
    """
    slow_delivery_notifications = is_delivery_slow_for_providers(
        threshold=0.3,
        created_at=datetime.utcnow() - timedelta(minutes=10),
        delivery_time=timedelta(minutes=4),
    )

    # only adjust if some values are true and some are false - ie, don't adjust if all providers are fast or
    # all providers are slow
    if len(set(slow_delivery_notifications.values())) != 1:
        for provider_name, is_slow in slow_delivery_notifications.items():
            if is_slow:
                current_app.logger.warning('Slow delivery notifications detected for provider {}'.format(provider_name))
                dao_reduce_sms_provider_priority(provider_name, time_threshold=timedelta(minutes=10))
def test_reduce_sms_provider_priority_does_nothing_if_providers_have_recently_changed(
    mocker,
    restore_provider_details,
):
    mock_is_slow = mocker.patch(
        'app.celery.scheduled_tasks.is_delivery_slow_for_providers')
    mock_reduce = mocker.patch(
        'app.celery.scheduled_tasks.dao_reduce_sms_provider_priority')
    ProviderDetails.query.filter(
        ProviderDetails.identifier == 'firetext').update(
            {'updated_at': datetime.min})
    ProviderDetails.query.filter(ProviderDetails.identifier == 'mmg').update(
        {'updated_at': datetime.utcnow() - timedelta(minutes=4, seconds=59)})

    dao_reduce_sms_provider_priority('firetext',
                                     time_threshold=timedelta(minutes=5))

    assert mock_is_slow.called is False
    assert mock_reduce.called is False