Fix the API bulk notifications priority being ignored - #3030
Conversation
There was a problem hiding this comment.
Pull request overview
Fixes incorrect Celery queue routing for bulk-saved notifications where a stale template variable could cause mixed-priority batches to be sent to the wrong email queue (ignoring per-notification priority).
Changes:
- Update
try_to_send_notifications_to_queueto prefer each notification’s ownqueue_namebefore falling back to the batch-level template queue. - Add targeted regression tests covering mixed-priority batches, override precedence, and legacy fallback behavior.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| app/celery/tasks.py | Adjusts queue resolution logic in try_to_send_notifications_to_queue to avoid stale-template routing and preserve per-notification queueing. |
| tests/app/celery/test_tasks.py | Adds a new regression test suite validating correct per-notification queue routing and override/fallback behavior. |
Suppressed comments (3)
tests/app/celery/test_tasks.py:565
- Same key-type issue as above:
notification_id_queueshould reflect the real payload shape (string IDs), otherwise this test may pass even if the override lookup fails in production.
saved_notifications = [self._make_notification(notification_id, QueueNames.SEND_EMAIL_HIGH)]
notification_id_queue = {notification_id: QueueNames.SEND_EMAIL_LOW}
tests/app/celery/test_tasks.py:583
- Same key-type issue as above:
notification_id_queueis typically keyed by string IDs, so the fallback-path test should usestr(notification_id)as the key to match production behavior.
saved_notifications = [self._make_notification(notification_id, None)]
notification_id_queue = {notification_id: None}
tests/app/celery/test_tasks.py:609
- Same key-type issue as above: use string IDs for
notification_id_queueto match the signed payload shape, otherwise this regression test may not reflect the production behavior.
self._make_notification(normal_id, QueueNames.SEND_EMAIL_MEDIUM),
]
notification_id_queue = {n.id: None for n in saved_notifications}
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| priority_call = send_mock.call_args_list[3] | ||
| assert priority_call == call(saved_notifications[3], False, QueueNames.SEND_EMAIL_HIGH) | ||
|
|
||
| def test_lookup_is_robust_to_notification_id_being_a_uuid_or_string(self, notify_api, mocker): |
There was a problem hiding this comment.
This is the test that targets the new bug that was found by the AI reviewer in this PR. Different from the original bug that this PR also targets (i.e. missing/lost priority type for bulk API notifications).
| # CSV bulk-redirect override (only useful for CSV jobs) | ||
| notification_id_queue.get(str(notification_obj.id)) | ||
| # per-notification correct value from persist_notifications | ||
| or notification_obj.queue_name |
| # cast makes the lookup robust either way. | ||
| queue = ( | ||
| # CSV bulk-redirect override (only useful for CSV jobs) | ||
| notification_id_queue.get(str(notification_obj.id)) |
andrewleith
left a comment
There was a problem hiding this comment.
This seems reasonable to me for email. Any reason this fix should not also be applied to the SMS code? Doesn't that have the same issue?
|
@andrewleith Hmm I was under the impression that the |
… into fix/wiped-notification-priority
Summary | Résumé
Fixes a bug in the batch-save path that caused priority notifications to be routed to the wrong Celery queue. In
try_to_send_notifications_to_queue, thetemplateloop variable leaked out of the batch loop, so every notification in a mixed-priority batch was routed as if it had the last notification's priority. A priority email batched with a normal email would land onsend-email-mediumand warnqueue_name send-email-high but was sent to queue send-email-medium.Each notification is now routed by its own
queue_name(correctly set per-notification inpersist_notifications), so mixed-priority batches route correctly. The queue-override map has been left keyed by strings, matching the in-session type ofnotification.idinside these functions; a defensivestr()cast at the lookup site makes the code robust if a future refactor causes.idto be exposed as auuid.UUID.Related Issues | Cartes liées
What changed
app/celery/tasks.pytry_to_send_notifications_to_queue: fall back tonotification.queue_namebefore the stale batch template, so mixed-priority batches route correctly. Added aTODOnoting that the override map can be removed oncepersist_notificationshandles the CSV bulk-redirect rule.str()cast on the map lookup:notification_id_queue.get(str(notification_obj.id)). Insidesave_smss/save_emails,notification.idis a string in-session (SQLAlchemy'sbulk_save_objectsdoesn't refresh objects), so the lookup works as before — but the cast keeps it robust if a future change ever refreshes the object and turns.idinto auuid.UUID. Added a short comment explaining the ambiguity, plus aDict[str, Optional[str]]type hint on the map for clarity.tests/app/celery/test_tasks.pyNew
TestTryToSendNotificationsToQueueclass with 5 tests:test_uses_each_notifications_own_queue_name_in_mixed_priority_batch— mixed batch routes each notification to its own queue.test_notification_id_queue_override_wins_over_queue_name— CSV bulk-redirect override takes precedence when set.test_falls_back_to_template_queue_when_queue_name_and_map_are_both_empty— legacy fallback path.test_priority_notification_not_routed_to_last_batch_notifications_queue— direct regression for the production bug (mirrors the 5-notification batch from the log).test_lookup_is_robust_to_notification_id_being_a_uuid_or_string— documents the defensivestr()cast so the lookup keeps working whethernotification.idis a string (current behavior) or auuid.UUID(future).Also updated one existing
TestSaveEmailstest expectation to reflect the new (correct) behavior where a mockedchoose_queuereturn value now actually flows through to the delivery call.Impact / risk
Test instructions | Instructions pour tester la modification
Run the new regression tests:
All 5 tests should pass.
Run the broader batch-save tests to confirm no regressions:
After deploying to staging, verify with CloudWatch Logs Insights that the
has queue_name send-email-high but was sent to queue send-email-mediumwarning no longer appears for mixed-priority batches:Release Instructions | Instructions pour le déploiement
None.
Reviewer checklist | Liste de vérification du réviseur