-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Track notification counts per thread (implement MSC3773) #13181
Conversation
80d632a
to
23a632e
Compare
b325dfc
to
067563b
Compare
a6d67c2
to
9b92fce
Compare
405e030
to
7d3e937
Compare
7d3e937
to
d56296a
Compare
synapse/storage/schema/main/delta/72/03thread_notifications.sql
Outdated
Show resolved
Hide resolved
…tions_by_room_for_user.
# XXX All threads should have the same stream ordering? | ||
max_summary_stream_ordering = max( | ||
summary_stream_ordering, max_summary_stream_ordering | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to figure out what's going on here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at my test server a bit, this isn't true (that all event_push_summary
for a room/user have the same stream_ordering
). I'm not really sure why I thought this, but it likely causes subtle bugs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that stream ordering is the "max" stream ordering that we rotated from the event_push_actions table:
I think that means its equivalent to the stream ordering we rotated up to? They may differ but there should never be any relevant rows in EPA between the per-thread stream ordering and the max stream ordering?
# TODO Delete zeroed out threads completely from the database. | ||
elif notif_count or unread_count: | ||
thread_counts[thread_id] = NotifCounts( | ||
notify_count=notif_count, unread_count=unread_count | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be worth looking at this again briefly not that some other bugs have been fixed.
Requesting review of this, but keeping it in draft. I think the implementation is far enough along that we could merge this, but don't want it merged accidentally. @erikjohnston had a brief conversation of "how bad would it be to back this out if perf tanks". I think it wouldn't be too bad: backing out the code should essentially revert behavior, with the caveat there would be an additional unused column getting tossed around. This column could then be dropped without an issue. |
@@ -181,6 +186,174 @@ def _mark_read(event_id: str) -> None: | |||
_rotate() | |||
_assert_counts(0, 0, 0) | |||
|
|||
def test_count_aggregation_threads(self) -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I should possibly put this in a docstring...)
This is a "copy" of the test_count_aggregation
test, but adapted to have both a "main" timeline and a thread in the same room.
(Oh, woops, I hit the merge develop branch on the wrong PR, sowwy) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this looks sane
"stream_ordering": old_rotate_stream_ordering, | ||
"last_receipt_stream_ordering": stream_ordering, | ||
}, | ||
) | ||
|
||
# Then any updated threads get their notification count and unread | ||
# count updated. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This presumably handles the "main" thread too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm actually having trouble convincing myself this doesn't need to handle thread_id
being null separately.
If the summary exists above for the "main" thread than the upsert would fail, I think? (Since it would attempt to add a new row since null isn't equivalent null.)
I think that's possible, but probably not exercised by our tests?
} | ||
|
||
# simple_upsert_many_txn doesn't support a predicate clause to force using | ||
# the partial index (thread_id = NULL). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't that easy to add?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried a bit and was having issues with figuring out how the API should look -- a simple implementation which just accepts a string is pretty easy (I think), but a more complicated one which accepts keyvalues: Dict[str, Any]
is a bit harder because you need to do conversion of = NULL
to IS NULL
. I'm a bit surprised we don't already have code for this?
Anyway, it is doable, yes. I can do it as a separate PR if you'd like.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, never mind if it isn't trivial. Was just a bit surprised is all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at it, it should be doable. It would really simplify this code, so will take a look. 👍
# XXX All threads should have the same stream ordering? | ||
max_summary_stream_ordering = max( | ||
summary_stream_ordering, max_summary_stream_ordering | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that stream ordering is the "max" stream ordering that we rotated from the event_push_actions table:
I think that means its equivalent to the stream ordering we rotated up to? They may differ but there should never be any relevant rows in EPA between the per-thread stream ordering and the max stream ordering?
|
||
-- Update the unique index for `event_push_summary`. | ||
INSERT INTO background_updates (ordering, update_name, progress_json) VALUES | ||
(7003, 'event_push_summary_unique_index2', '{}'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit worried that a bunch of the code will break while we don't have the proper index on event_push_summary
? In particular I think if it encounters threads will try and insert multiple rows and fail due to the existing unique index?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that might be an issue -- would the solution be to drop the current index here and then add then new one in the background?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think so, as we need a unique index to have upserts work? I have a horrible suspicion that we might need to either do this as a two step PR, one to add the column and new unique index, and the other to drop the index and start populating the thread rows. Either that or have a flag that changes the behaviour from the current behaviour to the new behaviour depending on if the index has finished being created?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Either of those seems doable. Do we have a preferred way of doing that? I think we'd have to wait for the new index regardless since we don't know for sure that an index was done just because we have a new version of Synapse?
|
||
ALTER TABLE event_push_actions_staging ADD COLUMN thread_id TEXT; | ||
|
||
ALTER TABLE event_push_actions ADD COLUMN thread_id TEXT; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was not planning to do a background update to "fix" the thread_id
for any existing event_push_actions
rows -- I think with the way we summarize that's a somewhat futile effort and it should correct itself over time.
ALTER TABLE event_push_actions_staging ADD COLUMN thread_id TEXT; | ||
|
||
ALTER TABLE event_push_actions ADD COLUMN thread_id TEXT; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if the thread_id
column should really live on the events
table and not event_push_actions
and event_push_actions_staging
? This would be a bigger migration, but potentially more useful in the future? I don't have a strong opinion though.
Track notification counts on a per-thread basis, implementing MSC3773.
The overall design of this is to add a
thread_id
column to theevent_push_actions
(+event_push_actions_staging
) andevent_push_summary
tables. This allows the results to be segmented by the "main" timeline (which is represented byNULL
in the database) and any other threads (which have the root event ID in thethread_id
column).When retrieving counts of notifications we can then segment based on the thread, this is opt-in for the client by providing a sync flag. In the case the client doesn't want separate threads we simply sum across all threads + the main timeline.
The summarization code also needs to be updated to be per thread, instead of just per room.
Please see MSC3773 for a description of the API changes.
Part of #12550
To Do