Skip to content

Design a Notification System, stage 10 of 11: change it

Write the sender

Write the core loop of an email sender: claim a delivery, check it is still wanted, respect the class's quota, send, and record the outcome. Handle transient, permanent and unknown outcomes differently.

System so far· 10 parts
123456789SERVICEProduct servicesLOG / STREAMEvent streamWORKERNotificationplannerDATABASENotifications DBQUEUEDelivery queuesWORKERChannel sendersEXTERNALEmail providerEXTERNALPush servicesSERVICEInbox APICLIENTWeb andmobile apps

Select a component to see what it is responsible for and which state it owns.

  1. 1Product services → Event stream: Domain events via outbox
  2. 2Event stream → Notification planner: Events, at least once
  3. 3Notification planner → Notifications DB: Insert notifications under dedupe keys
  4. 4Notification planner → Delivery queues: Deliveries by priority class
  5. 5Channel senders → Delivery queues: Claim deliveries
  6. 6Channel senders → Email provider: Send within quota
  7. 7Channel senders → Push services: Send to each device
  8. 8Web and mobile apps → Inbox API: Inbox and read state
  9. 9Inbox API → Notifications DB: Read notifications
  • Asynchronous
  • Request / response

What you need to know

0 of 2 checks done
  1. A delivery moves through a small set of states. Each move is a conditional update: it only happens if the row is in the expected state, so concurrent senders can't both make it.

    FromToWhen
    queuedsendinga sender claims it (with a fresh attempt token)
    sendingsentthe provider accepted it
    sendingsuppressedpreferences no longer allow it
    sendingfaileda permanent error, or a deliberate give-up
    sendingqueueda transient error; try later
  2. Check

    Why condition the final update on the attempt token, not just the delivery id?