Cache the result of fetching the room hierarchy over federation. (#10647)

This commit is contained in:
Patrick Cloke 2021-08-26 07:16:53 -04:00 committed by GitHub
parent b45cc1530b
commit 5548fe0978
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 63 additions and 36 deletions

1
changelog.d/10647.misc Normal file
View file

@ -0,0 +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.

View file

@ -111,6 +111,23 @@ class FederationClient(FederationBase):
reset_expiry_on_get=False, 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): def _clear_tried_cache(self):
"""Clear pdu_destination_tried cache""" """Clear pdu_destination_tried cache"""
now = self._clock.time_msec() now = self._clock.time_msec()
@ -1324,6 +1341,10 @@ class FederationClient(FederationBase):
remote servers remote servers
""" """
cached_result = self._get_room_hierarchy_cache.get((room_id, suggested_only))
if cached_result:
return cached_result
async def send_request( async def send_request(
destination: str, destination: str,
) -> Tuple[JsonDict, Sequence[JsonDict], Sequence[str]]: ) -> Tuple[JsonDict, Sequence[JsonDict], Sequence[str]]:
@ -1370,20 +1391,23 @@ class FederationClient(FederationBase):
return room, children, inaccessible_children return room, children, inaccessible_children
try: try:
return await self._try_destination_list( result = await self._try_destination_list(
"fetch room hierarchy", "fetch room hierarchy",
destinations, destinations,
send_request, send_request,
failover_on_unknown_endpoint=True, failover_on_unknown_endpoint=True,
) )
except SynapseError as e: 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 # Fallback to the old federation API and translate the results if
# no servers implement the new API. # no servers implement the new API.
# #
# The algorithm below is a bit inefficient as it only attempts to # 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. # return additional layers.
if e.code == 502:
legacy_result = await self.get_space_summary( legacy_result = await self.get_space_summary(
destinations, destinations,
room_id, room_id,
@ -1419,9 +1443,11 @@ class FederationClient(FederationBase):
# It isn't clear from the response whether some of the rooms are # It isn't clear from the response whether some of the rooms are
# not accessible. # not accessible.
return requested_room, children, () result = (requested_room, children, ())
raise # 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) @attr.s(frozen=True, slots=True, auto_attribs=True)