Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Overriding retry policy for SMS high priority #2008

Merged
merged 33 commits into from
Nov 6, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
d5ce6a2
Overriding retry policy for SMS high priority
jimleroyer Oct 24, 2023
1099d85
Added comments + format
jimleroyer Oct 24, 2023
990f339
Fixed ordering
jimleroyer Oct 24, 2023
89731a3
mypy: Added typing to the untyped dict
jimleroyer Oct 25, 2023
00457b7
mypy again
jimleroyer Oct 25, 2023
4804bbb
Fix existing test with new changes
jimleroyer Oct 25, 2023
2d95aa0
Fix existing test with new changes
jimleroyer Oct 25, 2023
6cdd740
Merge remote-tracking branch 'origin/main' into feat/decrease-retry-f…
jimleroyer Oct 26, 2023
2dc05b8
Refactoring: rename method
jimleroyer Oct 26, 2023
77dd00f
Refactoring: granular params
jimleroyer Oct 26, 2023
d80087a
Added test around params building for delivery tasks
jimleroyer Oct 26, 2023
eaebf11
Changed test for correct value of retry period for sms high
jimleroyer Oct 26, 2023
cba4269
Introducing feature flag for celery retry policies
jimleroyer Oct 27, 2023
0a5d388
Changed FF name for custom task celery params
jimleroyer Oct 27, 2023
5a144f8
Adding extension to center editor on kb cursor
jimleroyer Oct 27, 2023
74834f7
Forgot the test config to activate env var
jimleroyer Oct 30, 2023
9cc04c2
Use constants in tests
jimleroyer Oct 30, 2023
b51f63f
Reversing env var config enabling logic (but same config)
jimleroyer Oct 30, 2023
dc81e0d
Arf got trick by Python conditional logic
jimleroyer Oct 30, 2023
d36e65f
Print that env var because it aint working
jimleroyer Oct 30, 2023
2fd7be1
Format
jimleroyer Oct 30, 2023
7737bfc
Removing print debug statement
jimleroyer Oct 30, 2023
43b4c47
Merge branch 'main' into feat/decrease-retry-for-sms-high
jimleroyer Nov 1, 2023
77b436f
Apply low retry period for the sms high priority lane on celery retry…
jimleroyer Nov 6, 2023
662112e
Merge remote-tracking branch 'origin/main' into feat/decrease-retry-f…
jimleroyer Nov 6, 2023
ff09bed
Merge remote-tracking branch 'origin/feat/decrease-retry-for-sms-high…
jimleroyer Nov 6, 2023
687b790
Refactoring retry periods for sms high priority
jimleroyer Nov 6, 2023
ad01cfd
Fix test
jimleroyer Nov 6, 2023
c82785f
Fix sort
jimleroyer Nov 6, 2023
887d4f3
Merge remote-tracking branch 'origin/main' into feat/decrease-retry-f…
jimleroyer Nov 6, 2023
57161f1
Added timonwong.shellcheck shell checker to devcontainer
jimleroyer Nov 6, 2023
d52c173
Removed manually raised exception only meant for testing
jimleroyer Nov 6, 2023
d655af3
Merge remote-tracking branch 'origin/main' into feat/decrease-retry-f…
jimleroyer Nov 6, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/delivery/send_to_providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ def send_sms_to_provider(notification):
inactive_service_failure(notification=notification)
return

# If the notification was not sent already, the status should be created.
if notification.status == "created":
provider = provider_to_use(
SMS_TYPE,
Expand Down Expand Up @@ -215,6 +216,8 @@ def send_email_to_provider(notification: Notification):
if not service.active:
inactive_service_failure(notification=notification)
return

# If the notification was not sent already, the status should be created.
if notification.status == "created":
provider = provider_to_use(EMAIL_TYPE, notification.id)

Expand Down
26 changes: 26 additions & 0 deletions app/notifications/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from app.models import BULK, NORMAL, PRIORITY

# Default retry policy for all notifications.
RETRY_POLICY_DEFAULT = {
"max_retries": 48,
"interval_start": 300,
"interval_step": 300,
"interval_max": 300,
"retry_errors": None,
}

# Retry policy for high priority notifications.
RETRY_POLICY_HIGH = {
"max_retries": 48,
"interval_start": 25,
"interval_step": 25,
"interval_max": 25,
"retry_errors": None,
}

# Retry policies for each notification priority lanes.
RETRY_POLICIES = {
BULK: RETRY_POLICY_DEFAULT,
NORMAL: RETRY_POLICY_DEFAULT,
PRIORITY: RETRY_POLICY_HIGH,
}
27 changes: 25 additions & 2 deletions app/notifications/process_notifications.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import uuid
from datetime import datetime
from typing import List
from typing import Any, Dict, List

from flask import current_app
from notifications_utils.clients import redis
Expand Down Expand Up @@ -35,6 +35,7 @@
ScheduledNotification,
Service,
)
from app.notifications import RETRY_POLICIES, RETRY_POLICY_DEFAULT
from app.types import VerifiedNotification
from app.utils import get_delivery_queue_for_template, get_template_instance
from app.v2.errors import BadRequestError
Expand Down Expand Up @@ -210,7 +211,11 @@ def db_save_and_send_notification(notification: Notification):

deliver_task = choose_deliver_task(notification)
try:
deliver_task.apply_async([str(notification.id)], queue=notification.queue_name)
deliver_task.apply_async(
[str(notification.id)],
queue=notification.queue_name,
**build_delivery_task_params(notification.notification_type, notification.template.process_type),
)
except Exception:
dao_delete_notifications_by_id(notification.id)
raise
Expand All @@ -219,6 +224,24 @@ def db_save_and_send_notification(notification: Notification):
)


def build_delivery_task_params(notification_type: str, notification_process_type: str) -> Dict[str, Any]:
"""
Build task params for the sending parameter tasks.

If the notification is a high priority SMS, set the retry policy to retry every 25 seconds
else fall back to the default retry policy of retrying every 5 minutes.
"""
params: dict[str, Any] = {}
params["retry"] = True
# Overring the retry policy is only supported for SMS for now;
# email support coming later.
if notification_type == SMS_TYPE:
params["retry_policy"] = RETRY_POLICIES[notification_process_type]
else:
params["retry_policy"] = RETRY_POLICY_DEFAULT
return params


def choose_queue(notification, research_mode, queue=None) -> QueueNames:
if research_mode or notification.key_type == KEY_TYPE_TEST:
queue = QueueNames.RESEARCH_MODE
Expand Down
37 changes: 35 additions & 2 deletions tests/app/notifications/test_process_notification.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import datetime
import uuid
from typing import Any, Dict
from unittest.mock import call

import pytest
Expand All @@ -14,13 +15,20 @@
from app.config import QueueNames
from app.dao.service_sms_sender_dao import dao_update_service_sms_sender
from app.models import (
BULK,
EMAIL_TYPE,
LETTER_TYPE,
NORMAL,
PRIORITY,
SMS_TYPE,
Notification,
NotificationHistory,
ScheduledNotification,
Template,
)
from app.notifications import RETRY_POLICY_DEFAULT
from app.notifications.process_notifications import (
build_delivery_task_params,
choose_queue,
create_content_for_notification,
db_save_and_send_notification,
Expand Down Expand Up @@ -1051,7 +1059,9 @@ def test_db_save_and_send_notification_sends_to_queue(

db_save_and_send_notification(notification=notification)

mocked.assert_called_once_with([str(notification.id)], queue=expected_queue)
mocked.assert_called_once_with(
[str(notification.id)], queue=expected_queue, retry=True, retry_policy=RETRY_POLICY_DEFAULT
)

def test_db_save_and_send_notification_throws_exception_deletes_notification(
self, sample_template, sample_api_key, sample_job, mocker
Expand Down Expand Up @@ -1080,11 +1090,34 @@ def test_db_save_and_send_notification_throws_exception_deletes_notification(

with pytest.raises(Boto3Error):
db_save_and_send_notification(notification)
mocked.assert_called_once_with([(str(notification.id))], queue=QueueNames.SEND_SMS_MEDIUM)
mocked.assert_called_once_with(
[(str(notification.id))], queue=QueueNames.SEND_SMS_MEDIUM, retry=True, retry_policy=RETRY_POLICY_DEFAULT
)

assert Notification.query.count() == 0
assert NotificationHistory.query.count() == 0

@pytest.mark.parametrize(
("notification_type, process_type, expected_retry, expected_retry_period"),
[
(EMAIL_TYPE, BULK, 48, 300),
(EMAIL_TYPE, NORMAL, 48, 300),
(EMAIL_TYPE, PRIORITY, 48, 300),
(SMS_TYPE, BULK, 48, 300),
(SMS_TYPE, NORMAL, 48, 300),
(SMS_TYPE, PRIORITY, 48, 26),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of hard coding these values we could pull them from RETRY_POLICY_DEFAULT and RETRY_POLICY_HIGH

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed it to the config values. 👍

],
)
def test_delivery_task_parameters(self, notification_type, process_type, expected_retry, expected_retry_period):
params: Dict[str, Any] = build_delivery_task_params(notification_type, process_type)
assert params["retry"] is True

retry_policy: Dict[str, Any] = params["retry_policy"]
assert retry_policy["max_retries"] == expected_retry
assert retry_policy["interval_start"] == expected_retry_period
assert retry_policy["interval_step"] == expected_retry_period
assert retry_policy["interval_max"] == expected_retry_period

def test_db_save_and_send_notification_throws_exception_when_missing_template(self, sample_api_key, mocker):
mocker.patch("app.celery.provider_tasks.deliver_sms.apply_async")
assert Notification.query.count() == 0
Expand Down
Loading