4949from synapse .util .async_helpers import concurrently_execute
5050from synapse .util .caches .expiringcache import ExpiringCache
5151from synapse .util .caches .lrucache import LruCache
52- from synapse .util .caches .response_cache import ResponseCache
52+ from synapse .util .caches .response_cache import ResponseCache , ResponseCacheContext
5353from synapse .util .metrics import Measure , measure_func
5454from synapse .visibility import filter_events_for_client
5555
8383LAZY_LOADED_MEMBERS_CACHE_MAX_SIZE = 100
8484
8585
86+ SyncRequestKey = Tuple [Any , ...]
87+
88+
8689@attr .s (slots = True , frozen = True )
8790class SyncConfig :
8891 user = attr .ib (type = UserID )
8992 filter_collection = attr .ib (type = FilterCollection )
9093 is_guest = attr .ib (type = bool )
91- request_key = attr .ib (type = Tuple [ Any , ...] )
94+ request_key = attr .ib (type = SyncRequestKey )
9295 device_id = attr .ib (type = Optional [str ])
9396
9497
@@ -266,9 +269,9 @@ def __init__(self, hs: "HomeServer"):
266269 self .presence_handler = hs .get_presence_handler ()
267270 self .event_sources = hs .get_event_sources ()
268271 self .clock = hs .get_clock ()
269- self .response_cache = ResponseCache (
272+ self .response_cache : ResponseCache [ SyncRequestKey ] = ResponseCache (
270273 hs .get_clock (), "sync"
271- ) # type: ResponseCache[Tuple[Any, ...]]
274+ )
272275 self .state = hs .get_state_handler ()
273276 self .auth = hs .get_auth ()
274277 self .storage = hs .get_storage ()
@@ -307,16 +310,18 @@ async def wait_for_sync_for_user(
307310 since_token ,
308311 timeout ,
309312 full_state ,
313+ cache_context = True ,
310314 )
311315 logger .debug ("Returning sync response for %s" , user_id )
312316 return res
313317
314318 async def _wait_for_sync_for_user (
315319 self ,
316320 sync_config : SyncConfig ,
317- since_token : Optional [StreamToken ] = None ,
318- timeout : int = 0 ,
319- full_state : bool = False ,
321+ since_token : Optional [StreamToken ],
322+ timeout : int ,
323+ full_state : bool ,
324+ cache_context : ResponseCacheContext [SyncRequestKey ],
320325 ) -> SyncResult :
321326 if since_token is None :
322327 sync_type = "initial_sync"
@@ -343,13 +348,13 @@ async def _wait_for_sync_for_user(
343348 if timeout == 0 or since_token is None or full_state :
344349 # we are going to return immediately, so don't bother calling
345350 # notifier.wait_for_events.
346- result = await self .current_sync_for_user (
351+ result : SyncResult = await self .current_sync_for_user (
347352 sync_config , since_token , full_state = full_state
348353 )
349354 else :
350355
351- def current_sync_callback (before_token , after_token ):
352- return self .current_sync_for_user (sync_config , since_token )
356+ async def current_sync_callback (before_token , after_token ) -> SyncResult :
357+ return await self .current_sync_for_user (sync_config , since_token )
353358
354359 result = await self .notifier .wait_for_events (
355360 sync_config .user .to_string (),
@@ -358,6 +363,17 @@ def current_sync_callback(before_token, after_token):
358363 from_token = since_token ,
359364 )
360365
366+ # if nothing has happened in any of the users' rooms since /sync was called,
367+ # the resultant next_batch will be the same as since_token (since the result
368+ # is generated when wait_for_events is first called, and not regenerated
369+ # when wait_for_events times out).
370+ #
371+ # If that happens, we mustn't cache it, so that when the client comes back
372+ # with the same cache token, we don't immediately return the same empty
373+ # result, causing a tightloop. (#8518)
374+ if result .next_batch == since_token :
375+ cache_context .should_cache = False
376+
361377 if result :
362378 if sync_config .filter_collection .lazy_load_members ():
363379 lazy_loaded = "true"
0 commit comments