|
12 | 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13 | 13 | # See the License for the specific language governing permissions and
|
14 | 14 | # limitations under the License.
|
| 15 | + |
| 16 | +"""Responsible for storing and fetching push actions / notifications. |
| 17 | +
|
| 18 | +There are two main uses for push actions: |
| 19 | + 1. Sending out push to a user's device; and |
| 20 | + 2. Tracking per-room per-user notification counts (used in sync requests). |
| 21 | +
|
| 22 | +For the former we simply use the `event_push_actions` table, which contains all |
| 23 | +the calculated actions for a given user (which were calculated by the |
| 24 | +`BulkPushRuleEvaluator`). |
| 25 | +
|
| 26 | +For the latter we could simply count the number of rows in `event_push_actions` |
| 27 | +table for a given room/user, but in practice this is *very* heavyweight when |
| 28 | +there were a large number of notifications (due to e.g. the user never reading a |
| 29 | +room). Plus, keeping all push actions indefinitely uses a lot of disk space. |
| 30 | +
|
| 31 | +To fix these issues, we add a new table `event_push_summary` that tracks |
| 32 | +per-user per-room counts of all notifications that happened before a stream |
| 33 | +ordering S. Thus, to get the notification count for a user / room we can simply |
| 34 | +query a single row in `event_push_summary` and count the number of rows in |
| 35 | +`event_push_actions` with a stream ordering larger than S (and as long as S is |
| 36 | +"recent", the number of rows needing to be scanned will be small). |
| 37 | +
|
| 38 | +The `event_push_summary` table is updated via a background job that periodically |
| 39 | +chooses a new stream ordering S' (usually the latest stream ordering), counts |
| 40 | +all notifications in `event_push_actions` between the existing S and S', and |
| 41 | +adds them to the existing counts in `event_push_summary`. |
| 42 | +
|
| 43 | +This allows us to delete old rows from `event_push_actions` once those rows have |
| 44 | +been counted and added to `event_push_summary` (we call this process |
| 45 | +"rotation"). |
| 46 | +
|
| 47 | +
|
| 48 | +We need to handle when a user sends a read receipt to the room. Again this is |
| 49 | +done as a background process. For each receipt we clear the row in |
| 50 | +`event_push_summary` and count the number of notifications in |
| 51 | +`event_push_actions` that happened after the receipt but before S, and insert |
| 52 | +that count into `event_push_summary` (If the receipt happened *after* S then we |
| 53 | +simply clear the `event_push_summary`.) |
| 54 | +
|
| 55 | +Note that its possible that if the read receipt is for an old event the relevant |
| 56 | +`event_push_actions` rows will have been rotated and we get the wrong count |
| 57 | +(it'll be too low). We accept this as a rare edge case that is unlikely to |
| 58 | +impact the user much (since the vast majority of read receipts will be for the |
| 59 | +latest event). |
| 60 | +
|
| 61 | +The last complication is to handle the race where we request the notifications |
| 62 | +counts after a user sends a read receipt into the room, but *before* the |
| 63 | +background update handles the receipt (without any special handling the counts |
| 64 | +would be outdated). We fix this by including in `event_push_summary` the read |
| 65 | +receipt we used when updating `event_push_summary`, and every time we query the |
| 66 | +table we check if that matches the most recent read receipt in the room. If yes, |
| 67 | +continue as above, if not we simply query the `event_push_actions` table |
| 68 | +directly. |
| 69 | +
|
| 70 | +Since read receipts are almost always for recent events, scanning the |
| 71 | +`event_push_actions` table in this case is unlikely to be a problem. Even if it |
| 72 | +is a problem, it is temporary until the background job handles the new read |
| 73 | +receipt. |
| 74 | +""" |
| 75 | + |
15 | 76 | import logging
|
16 | 77 | from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, Union, cast
|
17 | 78 |
|
|
0 commit comments