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

Commit 5548fe0

Browse files
authored
Cache the result of fetching the room hierarchy over federation. (#10647)
1 parent b45cc15 commit 5548fe0

File tree

2 files changed

+67
-40
lines changed

2 files changed

+67
-40
lines changed

changelog.d/10647.misc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Improve the performance of the `/hierarchy` API (from [MSC2946](https://github.com/matrix-org/matrix-doc/pull/2946)) by caching responses received over federation.

synapse/federation/federation_client.py

+66-40
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,23 @@ def __init__(self, hs: "HomeServer"):
111111
reset_expiry_on_get=False,
112112
)
113113

114+
# A cache for fetching the room hierarchy over federation.
115+
#
116+
# Some stale data over federation is OK, but must be refreshed
117+
# periodically since the local server is in the room.
118+
#
119+
# It is a map of (room ID, suggested-only) -> the response of
120+
# get_room_hierarchy.
121+
self._get_room_hierarchy_cache: ExpiringCache[
122+
Tuple[str, bool], Tuple[JsonDict, Sequence[JsonDict], Sequence[str]]
123+
] = ExpiringCache(
124+
cache_name="get_room_hierarchy_cache",
125+
clock=self._clock,
126+
max_len=1000,
127+
expiry_ms=5 * 60 * 1000,
128+
reset_expiry_on_get=False,
129+
)
130+
114131
def _clear_tried_cache(self):
115132
"""Clear pdu_destination_tried cache"""
116133
now = self._clock.time_msec()
@@ -1324,6 +1341,10 @@ async def get_room_hierarchy(
13241341
remote servers
13251342
"""
13261343

1344+
cached_result = self._get_room_hierarchy_cache.get((room_id, suggested_only))
1345+
if cached_result:
1346+
return cached_result
1347+
13271348
async def send_request(
13281349
destination: str,
13291350
) -> Tuple[JsonDict, Sequence[JsonDict], Sequence[str]]:
@@ -1370,58 +1391,63 @@ async def send_request(
13701391
return room, children, inaccessible_children
13711392

13721393
try:
1373-
return await self._try_destination_list(
1394+
result = await self._try_destination_list(
13741395
"fetch room hierarchy",
13751396
destinations,
13761397
send_request,
13771398
failover_on_unknown_endpoint=True,
13781399
)
13791400
except SynapseError as e:
1401+
# If an unexpected error occurred, re-raise it.
1402+
if e.code != 502:
1403+
raise
1404+
13801405
# Fallback to the old federation API and translate the results if
13811406
# no servers implement the new API.
13821407
#
13831408
# The algorithm below is a bit inefficient as it only attempts to
1384-
# get information for the requested room, but the legacy API may
1409+
# parse information for the requested room, but the legacy API may
13851410
# return additional layers.
1386-
if e.code == 502:
1387-
legacy_result = await self.get_space_summary(
1388-
destinations,
1389-
room_id,
1390-
suggested_only,
1391-
max_rooms_per_space=None,
1392-
exclude_rooms=[],
1393-
)
1411+
legacy_result = await self.get_space_summary(
1412+
destinations,
1413+
room_id,
1414+
suggested_only,
1415+
max_rooms_per_space=None,
1416+
exclude_rooms=[],
1417+
)
13941418

1395-
# Find the requested room in the response (and remove it).
1396-
for _i, room in enumerate(legacy_result.rooms):
1397-
if room.get("room_id") == room_id:
1398-
break
1399-
else:
1400-
# The requested room was not returned, nothing we can do.
1401-
raise
1402-
requested_room = legacy_result.rooms.pop(_i)
1403-
1404-
# Find any children events of the requested room.
1405-
children_events = []
1406-
children_room_ids = set()
1407-
for event in legacy_result.events:
1408-
if event.room_id == room_id:
1409-
children_events.append(event.data)
1410-
children_room_ids.add(event.state_key)
1411-
# And add them under the requested room.
1412-
requested_room["children_state"] = children_events
1413-
1414-
# Find the children rooms.
1415-
children = []
1416-
for room in legacy_result.rooms:
1417-
if room.get("room_id") in children_room_ids:
1418-
children.append(room)
1419-
1420-
# It isn't clear from the response whether some of the rooms are
1421-
# not accessible.
1422-
return requested_room, children, ()
1423-
1424-
raise
1419+
# Find the requested room in the response (and remove it).
1420+
for _i, room in enumerate(legacy_result.rooms):
1421+
if room.get("room_id") == room_id:
1422+
break
1423+
else:
1424+
# The requested room was not returned, nothing we can do.
1425+
raise
1426+
requested_room = legacy_result.rooms.pop(_i)
1427+
1428+
# Find any children events of the requested room.
1429+
children_events = []
1430+
children_room_ids = set()
1431+
for event in legacy_result.events:
1432+
if event.room_id == room_id:
1433+
children_events.append(event.data)
1434+
children_room_ids.add(event.state_key)
1435+
# And add them under the requested room.
1436+
requested_room["children_state"] = children_events
1437+
1438+
# Find the children rooms.
1439+
children = []
1440+
for room in legacy_result.rooms:
1441+
if room.get("room_id") in children_room_ids:
1442+
children.append(room)
1443+
1444+
# It isn't clear from the response whether some of the rooms are
1445+
# not accessible.
1446+
result = (requested_room, children, ())
1447+
1448+
# Cache the result to avoid fetching data over federation every time.
1449+
self._get_room_hierarchy_cache[(room_id, suggested_only)] = result
1450+
return result
14251451

14261452

14271453
@attr.s(frozen=True, slots=True, auto_attribs=True)

0 commit comments

Comments
 (0)