Skip to content

Commit 6c2cf0d

Browse files
committed
Ensure that the last_seen_ts is correctly updated
1 parent 5604d3a commit 6c2cf0d

File tree

3 files changed

+146
-3
lines changed

3 files changed

+146
-3
lines changed

synapse/handlers/sliding_sync/__init__.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1183,11 +1183,11 @@ async def get_room_sync_data(
11831183
# loaded members is enabled.
11841184
#
11851185
# We may later update this to account for previously sent members.
1186-
returned_users = {}
1186+
returned_user_id_to_last_seen_ts_map = {}
11871187
if lazy_load_room_members:
1188-
returned_users = dict.fromkeys(lazy_load_user_ids)
1188+
returned_user_id_to_last_seen_ts_map = dict.fromkeys(lazy_load_user_ids)
11891189
new_connection_state.room_lazy_membership[room_id] = RoomLazyMembershipChanges(
1190-
returned_user_id_to_last_seen_ts_map=returned_users
1190+
returned_user_id_to_last_seen_ts_map=returned_user_id_to_last_seen_ts_map
11911191
)
11921192

11931193
if initial:
@@ -1223,6 +1223,15 @@ async def get_room_sync_data(
12231223
user_ids=all_required_user_state,
12241224
)
12251225
)
1226+
1227+
# Update the room lazy membership changes to track which
1228+
# lazy loaded members were needed for this sync. This is so
1229+
# that we can correctly track the last time we sent down
1230+
# users' membership (and so can evict old membership state
1231+
# from the DB tables).
1232+
returned_user_id_to_last_seen_ts_map.update(
1233+
previously_returned_user_to_last_seen
1234+
)
12261235
else:
12271236
previously_returned_user_to_last_seen = {}
12281237

synapse/storage/databases/main/sliding_sync.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@
4949
logger = logging.getLogger(__name__)
5050

5151

52+
# How often to update the last seen timestamp for lazy members. We don't want to
53+
# update it too often as that causes DB writes.
54+
LAZY_MEMBERS_UPDATE_INTERVAL_MS = ONE_HOUR_SECONDS * MILLISECONDS_PER_SECOND
55+
56+
5257
class SlidingSyncStore(SQLBaseStore):
5358
def __init__(
5459
self,

tests/rest/client/sliding_sync/test_rooms_required_state.py

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@
2323
from synapse.handlers.sliding_sync import StateValues
2424
from synapse.rest.client import knock, login, room, sync
2525
from synapse.server import HomeServer
26+
from synapse.storage.databases.main.sliding_sync import LAZY_MEMBERS_UPDATE_INTERVAL_MS
27+
from synapse.types import SlidingSyncStreamToken
2628
from synapse.util.clock import Clock
29+
from synapse.util.constants import MILLISECONDS_PER_SECOND
2730

2831
from tests.rest.client.sliding_sync.test_sliding_sync import SlidingSyncBase
2932
from tests.test_utils.event_injection import mark_event_as_partial_state
@@ -1932,3 +1935,129 @@ def test_rooms_required_state_expand_deduplicate(self) -> None:
19321935
# We should not see the room name again, as we have already sent that
19331936
# down.
19341937
self.assertIsNone(response_body["rooms"][room_id1].get("required_state"))
1938+
1939+
def test_lazy_loaded_last_seen_ts(self) -> None:
1940+
"""Test that the `last_seen_ts` column in
1941+
`sliding_sync_connection_lazy_members` is correctly kept up to date"""
1942+
1943+
user1_id = self.register_user("user1", "pass")
1944+
user1_tok = self.login(user1_id, "pass")
1945+
user2_id = self.register_user("user2", "pass")
1946+
user2_tok = self.login(user2_id, "pass")
1947+
1948+
room_id = self.helper.create_room_as(user2_id, tok=user2_tok, is_public=True)
1949+
self.helper.join(room_id, user1_id, tok=user1_tok)
1950+
1951+
# Send a message so that user1 comes down sync.
1952+
self.helper.send(room_id, "msg", tok=user1_tok)
1953+
1954+
sync_body = {
1955+
"lists": {
1956+
"foo-list": {
1957+
"ranges": [[0, 1]],
1958+
"required_state": [
1959+
[EventTypes.Member, StateValues.LAZY],
1960+
],
1961+
"timeline_limit": 1,
1962+
}
1963+
}
1964+
}
1965+
response_body, from_token = self.do_sync(sync_body, tok=user1_tok)
1966+
1967+
# Check that user1 is returned
1968+
state_map = self.get_success(
1969+
self.storage_controllers.state.get_current_state(room_id)
1970+
)
1971+
self._assertRequiredStateIncludes(
1972+
response_body["rooms"][room_id]["required_state"],
1973+
{
1974+
state_map[(EventTypes.Member, user1_id)],
1975+
},
1976+
exact=True,
1977+
)
1978+
1979+
# Check that we have an entry in sliding_sync_connection_lazy_members
1980+
connection_pos1 = self.get_success(
1981+
SlidingSyncStreamToken.from_string(self.store, from_token)
1982+
).connection_position
1983+
lazy_member_entries = self.get_success(
1984+
self.store.get_sliding_sync_connection_lazy_members(
1985+
connection_pos1, room_id, {user1_id}
1986+
)
1987+
)
1988+
self.assertIn(user1_id, lazy_member_entries)
1989+
1990+
prev_timestamp = lazy_member_entries[user1_id]
1991+
1992+
# If user1 is sent down again, the last_seen_ts should NOT be updated as
1993+
# not enough time has passed.
1994+
self.helper.send(room_id, "msg2", tok=user1_tok)
1995+
1996+
response_body, from_token = self.do_sync(
1997+
sync_body, since=from_token, tok=user1_tok
1998+
)
1999+
2000+
# We expect the required_state map to be empty as nothing has changed.
2001+
state_map = self.get_success(
2002+
self.storage_controllers.state.get_current_state(room_id)
2003+
)
2004+
self._assertRequiredStateIncludes(
2005+
response_body["rooms"][room_id].get("required_state", []),
2006+
{},
2007+
exact=True,
2008+
)
2009+
2010+
connection_pos2 = self.get_success(
2011+
SlidingSyncStreamToken.from_string(self.store, from_token)
2012+
).connection_position
2013+
2014+
lazy_member_entries = self.get_success(
2015+
self.store.get_sliding_sync_connection_lazy_members(
2016+
connection_pos2, room_id, {user1_id}
2017+
)
2018+
)
2019+
2020+
# The timestamp should be unchanged.
2021+
self.assertEqual(lazy_member_entries[user1_id], prev_timestamp)
2022+
2023+
# Now advance the time by `LAZY_MEMBERS_UPDATE_INTERVAL_MS` so that we
2024+
# would update the timestamp.
2025+
self.reactor.advance(LAZY_MEMBERS_UPDATE_INTERVAL_MS / MILLISECONDS_PER_SECOND)
2026+
2027+
# Send a message from user2
2028+
self.helper.send(room_id, "msg3", tok=user2_tok)
2029+
2030+
response_body, from_token = self.do_sync(
2031+
sync_body, since=from_token, tok=user1_tok
2032+
)
2033+
2034+
connection_pos3 = self.get_success(
2035+
SlidingSyncStreamToken.from_string(self.store, from_token)
2036+
).connection_position
2037+
2038+
lazy_member_entries = self.get_success(
2039+
self.store.get_sliding_sync_connection_lazy_members(
2040+
connection_pos3, room_id, {user1_id}
2041+
)
2042+
)
2043+
2044+
# The timestamp for user1 should be unchanged, as they were not sent down.
2045+
self.assertEqual(lazy_member_entries[user1_id], prev_timestamp)
2046+
2047+
# If user1 sends a message, then the timestamp should be updated.
2048+
self.helper.send(room_id, "msg4", tok=user1_tok)
2049+
2050+
response_body, from_token = self.do_sync(
2051+
sync_body, since=from_token, tok=user1_tok
2052+
)
2053+
connection_pos4 = self.get_success(
2054+
SlidingSyncStreamToken.from_string(self.store, from_token)
2055+
).connection_position
2056+
2057+
lazy_member_entries = self.get_success(
2058+
self.store.get_sliding_sync_connection_lazy_members(
2059+
connection_pos4, room_id, {user1_id}
2060+
)
2061+
)
2062+
# The timestamp for user1 should be updated.
2063+
self.assertGreater(lazy_member_entries[user1_id], prev_timestamp)

0 commit comments

Comments
 (0)