3333)
3434from synapse .storage .databases .main .cache import CacheInvalidationWorkerStore
3535from synapse .storage .util .id_generators import MultiWriterIdGenerator
36+ from synapse .types import EventOrderings
3637from synapse .util .caches .descriptors import cached
3738
3839if TYPE_CHECKING :
@@ -112,39 +113,48 @@ def process_replication_position(
112113
113114 @staticmethod
114115 def _should_skip_autosubscription_after_unsubscription (
115- autosub_stream_ordering : int ,
116- autosub_topological_ordering : int ,
117- unsubscribed_at_stream_ordering : int ,
118- unsubscribed_at_topological_ordering : int ,
116+ * ,
117+ autosub : EventOrderings ,
118+ unsubscribed_at : EventOrderings ,
119119 ) -> bool :
120120 """
121- Returns whether an automatic subscription occurring following an unsubscription
121+ Returns whether an automatic subscription occurring *after* an unsubscription
122122 should be skipped, because the unsubscription already 'acknowledges' the event
123123 causing the automatic subscription (the cause event).
124124
125+ To determine *after*, we use `stream_ordering` unless the event is backfilled
126+ (negative `stream_ordering`) and fallback to topological ordering.
127+
125128 Args:
126- autosub_stream_ordering: the stream_ordering of the cause event
127- autosub_topological_ordering: the topological_ordering of the cause event
128- unsubscribed_at_stream_ordering: the maximum stream ordering at the time of unsubscription
129- unsubscribed_at_topological_ordering: the maximum stream ordering at the time of unsubscription
129+ autosub: the stream_ordering and topological_ordering of the cause event
130+ unsubscribed_at:
131+ the maximum stream ordering and the maximum topological ordering at the time of unsubscription
130132
131133 Returns:
132134 True if the automatic subscription should be skipped
133135 """
134- # these two orderings should be positive, because they don't refer to a specific event
135- # but rather the maximum at the time of unsubscription
136- assert unsubscribed_at_stream_ordering > 0
137- assert unsubscribed_at_topological_ordering > 0
138-
139- if unsubscribed_at_stream_ordering >= autosub_stream_ordering > 0 :
136+ # For normal rooms, these two orderings should be positive, because
137+ # they don't refer to a specific event but rather the maximum at the
138+ # time of unsubscription.
139+ #
140+ # However, for rooms that have never been joined and that are being peeked at,
141+ # we might not have a single non-backfilled event and therefore the stream
142+ # ordering might be negative, so we don't assert this case.
143+ assert unsubscribed_at .topological > 0
144+
145+ unsubscribed_at_backfilled = unsubscribed_at .stream < 0
146+ if (
147+ not unsubscribed_at_backfilled
148+ and unsubscribed_at .stream >= autosub .stream > 0
149+ ):
140150 # non-backfilled events: the unsubscription is later according to
141151 # the stream
142152 return True
143153
144- if autosub_stream_ordering < 0 :
154+ if autosub . stream < 0 :
145155 # the auto-subscription cause event was backfilled, so fall back to
146156 # topological ordering
147- if unsubscribed_at_topological_ordering >= autosub_topological_ordering :
157+ if unsubscribed_at . topological >= autosub . topological :
148158 return True
149159
150160 return False
@@ -155,20 +165,29 @@ async def subscribe_user_to_thread(
155165 room_id : str ,
156166 thread_root_event_id : str ,
157167 * ,
158- automatic_event_orderings : Optional [Tuple [ int , int ] ],
168+ automatic_event_orderings : Optional [EventOrderings ],
159169 ) -> Optional [Union [int , AutomaticSubscriptionConflicted ]]:
160170 """Updates a user's subscription settings for a specific thread root.
161171
162172 If no change would be made to the subscription, does not produce any database change.
163173
174+ Case-by-case:
175+ - if we already have an automatic subscription:
176+ - new automatic subscriptions will be no-ops (no database write),
177+ - new manual subscriptions will overwrite the automatic subscription
178+ - if we already have a manual subscription:
179+ we don't update (no database write) in either case, because:
180+ - the existing manual subscription wins over a new automatic subscription request
181+ - there would be no need to write a manual subscription because we already have one
182+
164183 Args:
165184 user_id: The ID of the user whose settings are being updated.
166185 room_id: The ID of the room the thread root belongs to.
167186 thread_root_event_id: The event ID of the thread root.
168- automatic_event_id :
187+ automatic_event_orderings :
169188 Value depends on whether the subscription was performed automatically by the user's client.
170189 For manual subscriptions: None.
171- For automatic subscriptions: (stream_ordering, topological_ordering) of the event.
190+ For automatic subscriptions: the orderings of the event.
172191
173192 Returns:
174193 If a subscription is made: (int) the stream ID for this update.
@@ -243,20 +262,19 @@ def _subscribe_user_to_thread_txn(
243262 # is good enough (either we already have a manual subscription,
244263 # or we requested an automatic subscription)
245264 # In that case, nothing to change here.
265+ # (See docstring for case-by-case explanation)
246266 return None
247267
248268 if not subscribed and requested_automatic :
249269 assert automatic_event_orderings is not None
250270 # we previously unsubscribed and we are now automatically subscribing
251271 # Check whether the new autosubscription should be skipped
252- autosub_stream_ordering , autosub_topological_ordering = (
253- automatic_event_orderings
254- )
255272 if ThreadSubscriptionsWorkerStore ._should_skip_autosubscription_after_unsubscription (
256- autosub_stream_ordering ,
257- autosub_topological_ordering ,
258- unsubscribed_at_stream_ordering ,
259- unsubscribed_at_topological_ordering ,
273+ autosub = automatic_event_orderings ,
274+ unsubscribed_at = EventOrderings (
275+ unsubscribed_at_stream_ordering ,
276+ unsubscribed_at_topological_ordering ,
277+ ),
260278 ):
261279 # skip the subscription
262280 return AutomaticSubscriptionConflicted ()
@@ -473,7 +491,7 @@ def get_max_thread_subscriptions_stream_id(self) -> int:
473491 return self ._thread_subscriptions_id_gen .get_current_token ()
474492
475493 async def get_updated_thread_subscriptions (
476- self , from_id : int , to_id : int , limit : int
494+ self , * , from_id : int , to_id : int , limit : int
477495 ) -> List [Tuple [int , str , str , str ]]:
478496 """Get updates to thread subscriptions between two stream IDs.
479497
@@ -506,7 +524,7 @@ def get_updated_thread_subscriptions_txn(
506524 )
507525
508526 async def get_updated_thread_subscriptions_for_user (
509- self , user_id : str , from_id : int , to_id : int , limit : int
527+ self , user_id : str , * , from_id : int , to_id : int , limit : int
510528 ) -> List [Tuple [int , str , str ]]:
511529 """Get updates to thread subscriptions for a specific user.
512530
0 commit comments