|
| 1 | +# ADR047: Compute submission status from delivery timestamps |
| 2 | + |
| 3 | +Date: 2025-12-04 |
| 4 | + |
| 5 | +## Status |
| 6 | + |
| 7 | +Proposed |
| 8 | + |
| 9 | +## Context |
| 10 | + |
| 11 | +We are making submission delivery asynchronous for all submission methods (email and S3) and we need a more robust way to track the delivery status of submissions in forms-runner. |
| 12 | + |
| 13 | +### Using the Submission model for all submission types |
| 14 | + |
| 15 | +To make S3 submissions asynchronous, we need to use the `Submission` model for S3 submissions. Currently, only email submissions create `Submissions`. The model only supports `pending` or `bounced` values for the `delivery_status` enum - there is no `delivered` status. This would make it difficult for S3 submission to use the `Submission` model, as we would need a way see which Submission are successful versus those that may need to be retried. |
| 16 | + |
| 17 | +### Handling race conditions with async notifications |
| 18 | + |
| 19 | +For email submissions, delivery and bounce notifications are processed asynchronously. This creates a race condition: we may receive and process these notifications out of order. Using timestamps for `delivered_at` and `bounced_at` allows us to accurately determine the sequence of events. We can only mark a submission as `delivered` if we can confirm that any bounce notification didn't occur after the successful delivery notification (i.e. an async bounce). |
| 20 | + |
| 21 | +## Decision |
| 22 | + |
| 23 | +We will replace the fixed `delivery_status` enum attribute with a computed `status` method that derives the submission status from `delivered_at` and `bounced_at` timestamp columns. |
| 24 | + |
| 25 | +The implementation will proceed in three phases: |
| 26 | + |
| 27 | +1. **Add new timestamp column**: Add the `bounced_at` attribute to the `Submission` model via a database migration (the `delivered_at` column already exists). |
| 28 | + |
| 29 | +2. **Switch to computed status**: Replace usage of `delivery_status` with the computed `status` method throughout the codebase. The `status` method will return one of three values: |
| 30 | + - `:pending` - when both `delivered_at` and `bounced_at` are nil |
| 31 | + - `:delivered` - when `delivered_at` is present and either `bounced_at` is nil or `delivered_at` is more recent |
| 32 | + - `:bounced` - when `bounced_at` is present and either `delivered_at` is nil or `bounced_at` is more recent |
| 33 | + |
| 34 | +The 'delivered_at' and 'bounced_at' should represent the most recent delivery attempt. I.e. set to nil when a new delivery attempt is made. |
| 35 | + |
| 36 | +3. **Remove legacy column**: Remove the `delivery_status` enum attribute and its database column. |
| 37 | + |
| 38 | +## Consequences |
| 39 | + |
| 40 | +### Positive |
| 41 | + |
| 42 | +- S3 submissions can use the Submission model. Adding a `delivered` status enables S3 submissions to use the same `Submission` model as email submissions, allowing us to easily see successful deliveries and those that need to be retried. This ultimately allows us to make S3 submissions asynchronous. |
| 43 | + |
| 44 | +- Correct handling of async bounce notifications. Timestamps allow us to accurately determine submission status even when delivery and bounce notifications are processed out of order. |
| 45 | + |
| 46 | +- Simplified logic for Submission deletion and retry. Determining which submissions to delete (those with `delivered` status) instead of finding "not_bounced" or assuming those that are "pending" are "delievered". |
| 47 | + |
| 48 | +- Provides an audit trail. Timestamps record when state changes occurred, providing more context than a single enum value. |
| 49 | + |
| 50 | +### Negative |
| 51 | + |
| 52 | +- Requires more complicated (and potentially slower) database queries to find delivered and bounced submissions. Not likely to cause issues out our current scale. |
| 53 | + |
| 54 | + |
| 55 | + |
| 56 | + |
0 commit comments