|
@@ -111,6 +111,23 @@ class FederationClient(FederationBase):
|
|
|
reset_expiry_on_get=False,
|
|
|
)
|
|
|
|
|
|
+ # A cache for fetching the room hierarchy over federation.
|
|
|
+ #
|
|
|
+ # Some stale data over federation is OK, but must be refreshed
|
|
|
+ # periodically since the local server is in the room.
|
|
|
+ #
|
|
|
+ # It is a map of (room ID, suggested-only) -> the response of
|
|
|
+ # get_room_hierarchy.
|
|
|
+ self._get_room_hierarchy_cache: ExpiringCache[
|
|
|
+ Tuple[str, bool], Tuple[JsonDict, Sequence[JsonDict], Sequence[str]]
|
|
|
+ ] = ExpiringCache(
|
|
|
+ cache_name="get_room_hierarchy_cache",
|
|
|
+ clock=self._clock,
|
|
|
+ max_len=1000,
|
|
|
+ expiry_ms=5 * 60 * 1000,
|
|
|
+ reset_expiry_on_get=False,
|
|
|
+ )
|
|
|
+
|
|
|
def _clear_tried_cache(self):
|
|
|
"""Clear pdu_destination_tried cache"""
|
|
|
now = self._clock.time_msec()
|
|
@@ -1324,6 +1341,10 @@ class FederationClient(FederationBase):
|
|
|
remote servers
|
|
|
"""
|
|
|
|
|
|
+ cached_result = self._get_room_hierarchy_cache.get((room_id, suggested_only))
|
|
|
+ if cached_result:
|
|
|
+ return cached_result
|
|
|
+
|
|
|
async def send_request(
|
|
|
destination: str,
|
|
|
) -> Tuple[JsonDict, Sequence[JsonDict], Sequence[str]]:
|
|
@@ -1370,58 +1391,63 @@ class FederationClient(FederationBase):
|
|
|
return room, children, inaccessible_children
|
|
|
|
|
|
try:
|
|
|
- return await self._try_destination_list(
|
|
|
+ result = await self._try_destination_list(
|
|
|
"fetch room hierarchy",
|
|
|
destinations,
|
|
|
send_request,
|
|
|
failover_on_unknown_endpoint=True,
|
|
|
)
|
|
|
except SynapseError as e:
|
|
|
+ # If an unexpected error occurred, re-raise it.
|
|
|
+ if e.code != 502:
|
|
|
+ raise
|
|
|
+
|
|
|
# Fallback to the old federation API and translate the results if
|
|
|
# no servers implement the new API.
|
|
|
#
|
|
|
# The algorithm below is a bit inefficient as it only attempts to
|
|
|
- # get information for the requested room, but the legacy API may
|
|
|
+ # parse information for the requested room, but the legacy API may
|
|
|
# return additional layers.
|
|
|
- if e.code == 502:
|
|
|
- legacy_result = await self.get_space_summary(
|
|
|
- destinations,
|
|
|
- room_id,
|
|
|
- suggested_only,
|
|
|
- max_rooms_per_space=None,
|
|
|
- exclude_rooms=[],
|
|
|
- )
|
|
|
+ legacy_result = await self.get_space_summary(
|
|
|
+ destinations,
|
|
|
+ room_id,
|
|
|
+ suggested_only,
|
|
|
+ max_rooms_per_space=None,
|
|
|
+ exclude_rooms=[],
|
|
|
+ )
|
|
|
|
|
|
- # Find the requested room in the response (and remove it).
|
|
|
- for _i, room in enumerate(legacy_result.rooms):
|
|
|
- if room.get("room_id") == room_id:
|
|
|
- break
|
|
|
- else:
|
|
|
- # The requested room was not returned, nothing we can do.
|
|
|
- raise
|
|
|
- requested_room = legacy_result.rooms.pop(_i)
|
|
|
-
|
|
|
- # Find any children events of the requested room.
|
|
|
- children_events = []
|
|
|
- children_room_ids = set()
|
|
|
- for event in legacy_result.events:
|
|
|
- if event.room_id == room_id:
|
|
|
- children_events.append(event.data)
|
|
|
- children_room_ids.add(event.state_key)
|
|
|
- # And add them under the requested room.
|
|
|
- requested_room["children_state"] = children_events
|
|
|
-
|
|
|
- # Find the children rooms.
|
|
|
- children = []
|
|
|
- for room in legacy_result.rooms:
|
|
|
- if room.get("room_id") in children_room_ids:
|
|
|
- children.append(room)
|
|
|
-
|
|
|
- # It isn't clear from the response whether some of the rooms are
|
|
|
- # not accessible.
|
|
|
- return requested_room, children, ()
|
|
|
-
|
|
|
- raise
|
|
|
+ # Find the requested room in the response (and remove it).
|
|
|
+ for _i, room in enumerate(legacy_result.rooms):
|
|
|
+ if room.get("room_id") == room_id:
|
|
|
+ break
|
|
|
+ else:
|
|
|
+ # The requested room was not returned, nothing we can do.
|
|
|
+ raise
|
|
|
+ requested_room = legacy_result.rooms.pop(_i)
|
|
|
+
|
|
|
+ # Find any children events of the requested room.
|
|
|
+ children_events = []
|
|
|
+ children_room_ids = set()
|
|
|
+ for event in legacy_result.events:
|
|
|
+ if event.room_id == room_id:
|
|
|
+ children_events.append(event.data)
|
|
|
+ children_room_ids.add(event.state_key)
|
|
|
+ # And add them under the requested room.
|
|
|
+ requested_room["children_state"] = children_events
|
|
|
+
|
|
|
+ # Find the children rooms.
|
|
|
+ children = []
|
|
|
+ for room in legacy_result.rooms:
|
|
|
+ if room.get("room_id") in children_room_ids:
|
|
|
+ children.append(room)
|
|
|
+
|
|
|
+ # It isn't clear from the response whether some of the rooms are
|
|
|
+ # not accessible.
|
|
|
+ result = (requested_room, children, ())
|
|
|
+
|
|
|
+ # Cache the result to avoid fetching data over federation every time.
|
|
|
+ self._get_room_hierarchy_cache[(room_id, suggested_only)] = result
|
|
|
+ return result
|
|
|
|
|
|
|
|
|
@attr.s(frozen=True, slots=True, auto_attribs=True)
|