mirror of
https://git.anonymousland.org/anonymousland/synapse.git
synced 2025-12-15 23:43:53 -05:00
Merge pull request #2970 from matrix-org/matthew/filter_members
Implement the lazy_load_members room state filter parameter
This commit is contained in:
commit
1bcd0490c2
5 changed files with 532 additions and 62 deletions
|
|
@ -1,5 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2015 - 2016 OpenMarket Ltd
|
||||
# Copyright 2015, 2016 OpenMarket Ltd
|
||||
# Copyright 2018 New Vector Ltd
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
|
@ -416,29 +417,44 @@ class SyncHandler(object):
|
|||
))
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def get_state_after_event(self, event):
|
||||
def get_state_after_event(self, event, types=None, filtered_types=None):
|
||||
"""
|
||||
Get the room state after the given event
|
||||
|
||||
Args:
|
||||
event(synapse.events.EventBase): event of interest
|
||||
types(list[(str, str|None)]|None): List of (type, state_key) tuples
|
||||
which are used to filter the state fetched. If `state_key` is None,
|
||||
all events are returned of the given type.
|
||||
May be None, which matches any key.
|
||||
filtered_types(list[str]|None): Only apply filtering via `types` to this
|
||||
list of event types. Other types of events are returned unfiltered.
|
||||
If None, `types` filtering is applied to all events.
|
||||
|
||||
Returns:
|
||||
A Deferred map from ((type, state_key)->Event)
|
||||
"""
|
||||
state_ids = yield self.store.get_state_ids_for_event(event.event_id)
|
||||
state_ids = yield self.store.get_state_ids_for_event(
|
||||
event.event_id, types, filtered_types=filtered_types,
|
||||
)
|
||||
if event.is_state():
|
||||
state_ids = state_ids.copy()
|
||||
state_ids[(event.type, event.state_key)] = event.event_id
|
||||
defer.returnValue(state_ids)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def get_state_at(self, room_id, stream_position):
|
||||
def get_state_at(self, room_id, stream_position, types=None, filtered_types=None):
|
||||
""" Get the room state at a particular stream position
|
||||
|
||||
Args:
|
||||
room_id(str): room for which to get state
|
||||
stream_position(StreamToken): point at which to get state
|
||||
types(list[(str, str|None)]|None): List of (type, state_key) tuples
|
||||
which are used to filter the state fetched. If `state_key` is None,
|
||||
all events are returned of the given type.
|
||||
filtered_types(list[str]|None): Only apply filtering via `types` to this
|
||||
list of event types. Other types of events are returned unfiltered.
|
||||
If None, `types` filtering is applied to all events.
|
||||
|
||||
Returns:
|
||||
A Deferred map from ((type, state_key)->Event)
|
||||
|
|
@ -453,7 +469,9 @@ class SyncHandler(object):
|
|||
|
||||
if last_events:
|
||||
last_event = last_events[-1]
|
||||
state = yield self.get_state_after_event(last_event)
|
||||
state = yield self.get_state_after_event(
|
||||
last_event, types, filtered_types=filtered_types,
|
||||
)
|
||||
|
||||
else:
|
||||
# no events in this room - so presumably no state
|
||||
|
|
@ -485,18 +503,42 @@ class SyncHandler(object):
|
|||
# TODO(mjark) Check for new redactions in the state events.
|
||||
|
||||
with Measure(self.clock, "compute_state_delta"):
|
||||
|
||||
types = None
|
||||
lazy_load_members = sync_config.filter_collection.lazy_load_members()
|
||||
filtered_types = None
|
||||
|
||||
if lazy_load_members:
|
||||
# We only request state for the members needed to display the
|
||||
# timeline:
|
||||
|
||||
types = [
|
||||
(EventTypes.Member, state_key)
|
||||
for state_key in set(
|
||||
event.sender # FIXME: we also care about invite targets etc.
|
||||
for event in batch.events
|
||||
)
|
||||
]
|
||||
|
||||
# only apply the filtering to room members
|
||||
filtered_types = [EventTypes.Member]
|
||||
|
||||
if full_state:
|
||||
if batch:
|
||||
current_state_ids = yield self.store.get_state_ids_for_event(
|
||||
batch.events[-1].event_id
|
||||
batch.events[-1].event_id, types=types,
|
||||
filtered_types=filtered_types,
|
||||
)
|
||||
|
||||
state_ids = yield self.store.get_state_ids_for_event(
|
||||
batch.events[0].event_id
|
||||
batch.events[0].event_id, types=types,
|
||||
filtered_types=filtered_types,
|
||||
)
|
||||
|
||||
else:
|
||||
current_state_ids = yield self.get_state_at(
|
||||
room_id, stream_position=now_token
|
||||
room_id, stream_position=now_token, types=types,
|
||||
filtered_types=filtered_types,
|
||||
)
|
||||
|
||||
state_ids = current_state_ids
|
||||
|
|
@ -511,18 +553,22 @@ class SyncHandler(object):
|
|||
timeline_start=state_ids,
|
||||
previous={},
|
||||
current=current_state_ids,
|
||||
lazy_load_members=lazy_load_members,
|
||||
)
|
||||
elif batch.limited:
|
||||
state_at_previous_sync = yield self.get_state_at(
|
||||
room_id, stream_position=since_token
|
||||
room_id, stream_position=since_token, types=types,
|
||||
filtered_types=filtered_types,
|
||||
)
|
||||
|
||||
current_state_ids = yield self.store.get_state_ids_for_event(
|
||||
batch.events[-1].event_id
|
||||
batch.events[-1].event_id, types=types,
|
||||
filtered_types=filtered_types,
|
||||
)
|
||||
|
||||
state_at_timeline_start = yield self.store.get_state_ids_for_event(
|
||||
batch.events[0].event_id
|
||||
batch.events[0].event_id, types=types,
|
||||
filtered_types=filtered_types,
|
||||
)
|
||||
|
||||
timeline_state = {
|
||||
|
|
@ -530,14 +576,35 @@ class SyncHandler(object):
|
|||
for event in batch.events if event.is_state()
|
||||
}
|
||||
|
||||
# TODO: optionally filter out redundant membership events at this
|
||||
# point, to stop repeatedly sending members in every /sync as if
|
||||
# the client isn't tracking them.
|
||||
# When implemented, this should filter using event_ids (not mxids).
|
||||
# In practice, limited syncs are
|
||||
# relatively rare so it's not a total disaster to send redundant
|
||||
# members down at this point. Redundant members are ones which
|
||||
# repeatedly get sent down /sync because we don't know if the client
|
||||
# is caching them or not.
|
||||
|
||||
state_ids = _calculate_state(
|
||||
timeline_contains=timeline_state,
|
||||
timeline_start=state_at_timeline_start,
|
||||
previous=state_at_previous_sync,
|
||||
current=current_state_ids,
|
||||
lazy_load_members=lazy_load_members,
|
||||
)
|
||||
else:
|
||||
state_ids = {}
|
||||
if lazy_load_members:
|
||||
# TODO: filter out redundant members based on their mxids (not their
|
||||
# event_ids) at this point. We know we can do it based on mxid as this
|
||||
# is an non-gappy incremental sync.
|
||||
|
||||
if types:
|
||||
state_ids = yield self.store.get_state_ids_for_event(
|
||||
batch.events[0].event_id, types=types,
|
||||
filtered_types=filtered_types,
|
||||
)
|
||||
|
||||
state = {}
|
||||
if state_ids:
|
||||
|
|
@ -1448,7 +1515,9 @@ def _action_has_highlight(actions):
|
|||
return False
|
||||
|
||||
|
||||
def _calculate_state(timeline_contains, timeline_start, previous, current):
|
||||
def _calculate_state(
|
||||
timeline_contains, timeline_start, previous, current, lazy_load_members,
|
||||
):
|
||||
"""Works out what state to include in a sync response.
|
||||
|
||||
Args:
|
||||
|
|
@ -1457,6 +1526,9 @@ def _calculate_state(timeline_contains, timeline_start, previous, current):
|
|||
previous (dict): state at the end of the previous sync (or empty dict
|
||||
if this is an initial sync)
|
||||
current (dict): state at the end of the timeline
|
||||
lazy_load_members (bool): whether to return members from timeline_start
|
||||
or not. assumes that timeline_start has already been filtered to
|
||||
include only the members the client needs to know about.
|
||||
|
||||
Returns:
|
||||
dict
|
||||
|
|
@ -1472,9 +1544,25 @@ def _calculate_state(timeline_contains, timeline_start, previous, current):
|
|||
}
|
||||
|
||||
c_ids = set(e for e in current.values())
|
||||
tc_ids = set(e for e in timeline_contains.values())
|
||||
p_ids = set(e for e in previous.values())
|
||||
ts_ids = set(e for e in timeline_start.values())
|
||||
p_ids = set(e for e in previous.values())
|
||||
tc_ids = set(e for e in timeline_contains.values())
|
||||
|
||||
# If we are lazyloading room members, we explicitly add the membership events
|
||||
# for the senders in the timeline into the state block returned by /sync,
|
||||
# as we may not have sent them to the client before. We find these membership
|
||||
# events by filtering them out of timeline_start, which has already been filtered
|
||||
# to only include membership events for the senders in the timeline.
|
||||
# In practice, we can do this by removing them from the p_ids list,
|
||||
# which is the list of relevant state we know we have already sent to the client.
|
||||
# see https://github.com/matrix-org/synapse/pull/2970
|
||||
# /files/efcdacad7d1b7f52f879179701c7e0d9b763511f#r204732809
|
||||
|
||||
if lazy_load_members:
|
||||
p_ids.difference_update(
|
||||
e for t, e in timeline_start.iteritems()
|
||||
if t[0] == EventTypes.Member
|
||||
)
|
||||
|
||||
state_ids = ((c_ids | ts_ids) - p_ids) - tc_ids
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue