Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit 1bcd049

Browse files
authored
Merge pull request #2970 from matrix-org/matthew/filter_members
Implement the lazy_load_members room state filter parameter
2 parents a4fe9d2 + bc7944e commit 1bcd049

File tree

5 files changed

+532
-62
lines changed

5 files changed

+532
-62
lines changed

changelog.d/2970.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
add support for the lazy_loaded_members filter as per MSC1227

synapse/api/filtering.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,10 @@
113113
},
114114
"contains_url": {
115115
"type": "boolean"
116-
}
116+
},
117+
"lazy_load_members": {
118+
"type": "boolean"
119+
},
117120
}
118121
}
119122

@@ -261,6 +264,9 @@ def presence_limit(self):
261264
def ephemeral_limit(self):
262265
return self._room_ephemeral_filter.limit()
263266

267+
def lazy_load_members(self):
268+
return self._room_state_filter.lazy_load_members()
269+
264270
def filter_presence(self, events):
265271
return self._presence_filter.filter(events)
266272

@@ -417,6 +423,9 @@ def filter(self, events):
417423
def limit(self):
418424
return self.filter_json.get("limit", 10)
419425

426+
def lazy_load_members(self):
427+
return self.filter_json.get("lazy_load_members", False)
428+
420429

421430
def _matches_wildcard(actual_value, filter_value):
422431
if filter_value.endswith("*"):

synapse/handlers/sync.py

Lines changed: 102 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
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

Comments
 (0)