Skip to content

Change how we count notifications in redis#2871

Open
jzbahrai wants to merge 1 commit into
mainfrom
task/fix-batch-processing-code
Open

Change how we count notifications in redis#2871
jzbahrai wants to merge 1 commit into
mainfrom
task/fix-batch-processing-code

Conversation

@jzbahrai

Copy link
Copy Markdown
Collaborator

Summary

The batch write pattern in process_ses_results:

  1. process_notifications() calls notifications_dao._update_notification_statuses(updates) — bulk-writes ALL N terminal statuses to the DB atomically
  2. Then loops over each notification calling update_annual_limit_and_bounce_rate()
    Inside that function, get_annual_limit_notifications_v3() is called which checks if seeding has occurred

The race condition:

  1. Notification Get rid of Gov.uk Branding in UI #1 in the batch: not yet seeded → seed_data_in_redis() fires → queries DB → finds ALL N notifications already at terminal status (from step 1) → seeds Redis with all N → did_we_seed=True → skips increment ✓
  2. Notification When using our copy of the notify repo we should connect it to our github actions/apps so we can automate as much testing as we can #2..N in the batch: already seeded → did_we_seed=False → increments count → but these were already counted during the seed → double count ✗

@jzbahrai jzbahrai requested a review from jimleroyer as a code owner May 22, 2026 19:09
Copilot AI review requested due to automatic review settings May 22, 2026 19:09
@jzbahrai jzbahrai changed the title Change Change how we count notifications in redis May 22, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR addresses a race condition in SES batch receipt processing that could double-count annual limit notification totals in Redis when seeding occurs during a batch update.

Changes:

  • Moves the annual-limit “seed state” check to run once per service after the batch DB write, then passes that seed result into per-notification counting logic.
  • Updates update_annual_limit_and_bounce_rate to accept did_we_seed from the caller instead of re-checking per notification.
  • Adds tests to validate that seeding during a batch prevents per-notification increments and that the seed check is performed once per service.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
app/celery/process_ses_receipts_tasks.py Checks annual-limit seed state once per service after batch DB update and uses it to prevent double counting.
tests/app/celery/test_process_ses_receipts_tasks.py Adds regression tests covering seeding vs incrementing behavior and ensuring one seed check per service.
Comments suppressed due to low confidence (1)

app/celery/process_ses_receipts_tasks.py:235

  • This log statement uses a double-quoted string literal inside an f-string expression (aws_response_dict["message"]). In Python this form raises a SyntaxError at import time; use single quotes for the key (aws_response_dict['message']) or assign the value to a variable before formatting.
    if not is_success:
        current_app.logger.info(f"{log_prefix} Delivery failed with error: {aws_response_dict["message"]}")


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +336 to +341
service_seed_states: Dict[str, bool] = {}
for _, notification, _ in receipts_with_notification_and_aws_response_dict:
sid = notification.service_id
if sid not in service_seed_states:
_, did_we_seed = get_annual_limit_notifications_v3(sid)
service_seed_states[sid] = did_we_seed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants