11# -*- coding: utf-8 -*-
2- # Copyright 2015 - 2016 OpenMarket Ltd
2+ # Copyright 2015, 2016 OpenMarket Ltd
3+ # Copyright 2018 New Vector Ltd
34#
45# Licensed under the Apache License, Version 2.0 (the "License");
56# you may not use this file except in compliance with the License.
@@ -416,29 +417,44 @@ def _load_filtered_recents(self, room_id, sync_config, now_token,
416417 ))
417418
418419 @defer .inlineCallbacks
419- def get_state_after_event (self , event ):
420+ def get_state_after_event (self , event , types = None , filtered_types = None ):
420421 """
421422 Get the room state after the given event
422423
423424 Args:
424425 event(synapse.events.EventBase): event of interest
426+ types(list[(str, str|None)]|None): List of (type, state_key) tuples
427+ which are used to filter the state fetched. If `state_key` is None,
428+ all events are returned of the given type.
429+ May be None, which matches any key.
430+ filtered_types(list[str]|None): Only apply filtering via `types` to this
431+ list of event types. Other types of events are returned unfiltered.
432+ If None, `types` filtering is applied to all events.
425433
426434 Returns:
427435 A Deferred map from ((type, state_key)->Event)
428436 """
429- state_ids = yield self .store .get_state_ids_for_event (event .event_id )
437+ state_ids = yield self .store .get_state_ids_for_event (
438+ event .event_id , types , filtered_types = filtered_types ,
439+ )
430440 if event .is_state ():
431441 state_ids = state_ids .copy ()
432442 state_ids [(event .type , event .state_key )] = event .event_id
433443 defer .returnValue (state_ids )
434444
435445 @defer .inlineCallbacks
436- def get_state_at (self , room_id , stream_position ):
446+ def get_state_at (self , room_id , stream_position , types = None , filtered_types = None ):
437447 """ Get the room state at a particular stream position
438448
439449 Args:
440450 room_id(str): room for which to get state
441451 stream_position(StreamToken): point at which to get state
452+ types(list[(str, str|None)]|None): List of (type, state_key) tuples
453+ which are used to filter the state fetched. If `state_key` is None,
454+ all events are returned of the given type.
455+ filtered_types(list[str]|None): Only apply filtering via `types` to this
456+ list of event types. Other types of events are returned unfiltered.
457+ If None, `types` filtering is applied to all events.
442458
443459 Returns:
444460 A Deferred map from ((type, state_key)->Event)
@@ -453,7 +469,9 @@ def get_state_at(self, room_id, stream_position):
453469
454470 if last_events :
455471 last_event = last_events [- 1 ]
456- state = yield self .get_state_after_event (last_event )
472+ state = yield self .get_state_after_event (
473+ last_event , types , filtered_types = filtered_types ,
474+ )
457475
458476 else :
459477 # no events in this room - so presumably no state
@@ -485,18 +503,42 @@ def compute_state_delta(self, room_id, batch, sync_config, since_token, now_toke
485503 # TODO(mjark) Check for new redactions in the state events.
486504
487505 with Measure (self .clock , "compute_state_delta" ):
506+
507+ types = None
508+ lazy_load_members = sync_config .filter_collection .lazy_load_members ()
509+ filtered_types = None
510+
511+ if lazy_load_members :
512+ # We only request state for the members needed to display the
513+ # timeline:
514+
515+ types = [
516+ (EventTypes .Member , state_key )
517+ for state_key in set (
518+ event .sender # FIXME: we also care about invite targets etc.
519+ for event in batch .events
520+ )
521+ ]
522+
523+ # only apply the filtering to room members
524+ filtered_types = [EventTypes .Member ]
525+
488526 if full_state :
489527 if batch :
490528 current_state_ids = yield self .store .get_state_ids_for_event (
491- batch .events [- 1 ].event_id
529+ batch .events [- 1 ].event_id , types = types ,
530+ filtered_types = filtered_types ,
492531 )
493532
494533 state_ids = yield self .store .get_state_ids_for_event (
495- batch .events [0 ].event_id
534+ batch .events [0 ].event_id , types = types ,
535+ filtered_types = filtered_types ,
496536 )
537+
497538 else :
498539 current_state_ids = yield self .get_state_at (
499- room_id , stream_position = now_token
540+ room_id , stream_position = now_token , types = types ,
541+ filtered_types = filtered_types ,
500542 )
501543
502544 state_ids = current_state_ids
@@ -511,33 +553,58 @@ def compute_state_delta(self, room_id, batch, sync_config, since_token, now_toke
511553 timeline_start = state_ids ,
512554 previous = {},
513555 current = current_state_ids ,
556+ lazy_load_members = lazy_load_members ,
514557 )
515558 elif batch .limited :
516559 state_at_previous_sync = yield self .get_state_at (
517- room_id , stream_position = since_token
560+ room_id , stream_position = since_token , types = types ,
561+ filtered_types = filtered_types ,
518562 )
519563
520564 current_state_ids = yield self .store .get_state_ids_for_event (
521- batch .events [- 1 ].event_id
565+ batch .events [- 1 ].event_id , types = types ,
566+ filtered_types = filtered_types ,
522567 )
523568
524569 state_at_timeline_start = yield self .store .get_state_ids_for_event (
525- batch .events [0 ].event_id
570+ batch .events [0 ].event_id , types = types ,
571+ filtered_types = filtered_types ,
526572 )
527573
528574 timeline_state = {
529575 (event .type , event .state_key ): event .event_id
530576 for event in batch .events if event .is_state ()
531577 }
532578
579+ # TODO: optionally filter out redundant membership events at this
580+ # point, to stop repeatedly sending members in every /sync as if
581+ # the client isn't tracking them.
582+ # When implemented, this should filter using event_ids (not mxids).
583+ # In practice, limited syncs are
584+ # relatively rare so it's not a total disaster to send redundant
585+ # members down at this point. Redundant members are ones which
586+ # repeatedly get sent down /sync because we don't know if the client
587+ # is caching them or not.
588+
533589 state_ids = _calculate_state (
534590 timeline_contains = timeline_state ,
535591 timeline_start = state_at_timeline_start ,
536592 previous = state_at_previous_sync ,
537593 current = current_state_ids ,
594+ lazy_load_members = lazy_load_members ,
538595 )
539596 else :
540597 state_ids = {}
598+ if lazy_load_members :
599+ # TODO: filter out redundant members based on their mxids (not their
600+ # event_ids) at this point. We know we can do it based on mxid as this
601+ # is an non-gappy incremental sync.
602+
603+ if types :
604+ state_ids = yield self .store .get_state_ids_for_event (
605+ batch .events [0 ].event_id , types = types ,
606+ filtered_types = filtered_types ,
607+ )
541608
542609 state = {}
543610 if state_ids :
@@ -1448,7 +1515,9 @@ def _action_has_highlight(actions):
14481515 return False
14491516
14501517
1451- def _calculate_state (timeline_contains , timeline_start , previous , current ):
1518+ def _calculate_state (
1519+ timeline_contains , timeline_start , previous , current , lazy_load_members ,
1520+ ):
14521521 """Works out what state to include in a sync response.
14531522
14541523 Args:
@@ -1457,6 +1526,9 @@ def _calculate_state(timeline_contains, timeline_start, previous, current):
14571526 previous (dict): state at the end of the previous sync (or empty dict
14581527 if this is an initial sync)
14591528 current (dict): state at the end of the timeline
1529+ lazy_load_members (bool): whether to return members from timeline_start
1530+ or not. assumes that timeline_start has already been filtered to
1531+ include only the members the client needs to know about.
14601532
14611533 Returns:
14621534 dict
@@ -1472,9 +1544,25 @@ def _calculate_state(timeline_contains, timeline_start, previous, current):
14721544 }
14731545
14741546 c_ids = set (e for e in current .values ())
1475- tc_ids = set (e for e in timeline_contains .values ())
1476- p_ids = set (e for e in previous .values ())
14771547 ts_ids = set (e for e in timeline_start .values ())
1548+ p_ids = set (e for e in previous .values ())
1549+ tc_ids = set (e for e in timeline_contains .values ())
1550+
1551+ # If we are lazyloading room members, we explicitly add the membership events
1552+ # for the senders in the timeline into the state block returned by /sync,
1553+ # as we may not have sent them to the client before. We find these membership
1554+ # events by filtering them out of timeline_start, which has already been filtered
1555+ # to only include membership events for the senders in the timeline.
1556+ # In practice, we can do this by removing them from the p_ids list,
1557+ # which is the list of relevant state we know we have already sent to the client.
1558+ # see https://github.com/matrix-org/synapse/pull/2970
1559+ # /files/efcdacad7d1b7f52f879179701c7e0d9b763511f#r204732809
1560+
1561+ if lazy_load_members :
1562+ p_ids .difference_update (
1563+ e for t , e in timeline_start .iteritems ()
1564+ if t [0 ] == EventTypes .Member
1565+ )
14781566
14791567 state_ids = ((c_ids | ts_ids ) - p_ids ) - tc_ids
14801568
0 commit comments