-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Support the stable endpoint from MSC2946 #11329
Changes from all commits
4ea9c9a
283cccd
b4ad0bf
db23830
1044250
8a98643
af60189
7e28306
b781047
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Support the stable API endpoints for [MSC2946](https://github.com/matrix-org/matrix-doc/pull/2946): the room `/hierarchy` endpoint. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -194,6 +194,7 @@ def _configure_named_resource( | |
{ | ||
"/_matrix/client/api/v1": client_resource, | ||
"/_matrix/client/r0": client_resource, | ||
"/_matrix/client/v1": client_resource, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For the generic worker it seems we just put There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's because of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (which is not to say there aren't way better ways of doing this and I would love it if this stuff were less of a mess. see also #5118) |
||
"/_matrix/client/v3": client_resource, | ||
"/_matrix/client/unstable": client_resource, | ||
"/_matrix/client/v2_alpha": client_resource, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1395,11 +1395,28 @@ async def get_room_hierarchy( | |
async def send_request( | ||
destination: str, | ||
) -> Tuple[JsonDict, Sequence[JsonDict], Sequence[str]]: | ||
res = await self.transport_layer.get_room_hierarchy( | ||
destination=destination, | ||
room_id=room_id, | ||
suggested_only=suggested_only, | ||
) | ||
try: | ||
res = await self.transport_layer.get_room_hierarchy( | ||
destination=destination, | ||
room_id=room_id, | ||
suggested_only=suggested_only, | ||
) | ||
except HttpResponseException as e: | ||
# If an error is received that is due to an unrecognised endpoint, | ||
# fallback to the unstable endpoint. Otherwise consider it a | ||
# legitmate error and raise. | ||
if not self._is_unknown_endpoint(e): | ||
raise | ||
|
||
logger.debug( | ||
"Couldn't fetch room hierarchy with the v1 API, falling back to the unstable API" | ||
) | ||
|
||
res = await self.transport_layer.get_room_hierarchy_unstable( | ||
destination=destination, | ||
room_id=room_id, | ||
suggested_only=suggested_only, | ||
) | ||
Comment on lines
+1398
to
+1419
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the same pattern we do with e.g. |
||
|
||
room = res.get("room") | ||
if not isinstance(room, dict): | ||
|
@@ -1449,6 +1466,10 @@ async def send_request( | |
if e.code != 502: | ||
raise | ||
|
||
logger.debug( | ||
"Couldn't fetch room hierarchy, falling back to the spaces API" | ||
) | ||
|
||
# Fallback to the old federation API and translate the results if | ||
# no servers implement the new API. | ||
# | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1192,10 +1192,24 @@ async def get_space_summary( | |
) | ||
|
||
async def get_room_hierarchy( | ||
self, | ||
destination: str, | ||
room_id: str, | ||
suggested_only: bool, | ||
self, destination: str, room_id: str, suggested_only: bool | ||
) -> JsonDict: | ||
Comment on lines
1194
to
+1196
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could choose whether to v1 vs. unstable based on a parameter but the other bits in this file seem to have separate methods for v1 vs. v2, etc. so I followed that pattern. I think we can clean that up separately if it makes sense? |
||
""" | ||
Args: | ||
destination: The remote server | ||
room_id: The room ID to ask about. | ||
suggested_only: if True, only suggested rooms will be returned | ||
""" | ||
path = _create_v1_path("/hierarchy/%s", room_id) | ||
|
||
return await self.client.get_json( | ||
destination=destination, | ||
path=path, | ||
args={"suggested_only": "true" if suggested_only else "false"}, | ||
) | ||
|
||
async def get_room_hierarchy_unstable( | ||
self, destination: str, room_id: str, suggested_only: bool | ||
) -> JsonDict: | ||
""" | ||
Args: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,8 +36,9 @@ | |
SynapseError, | ||
UnsupportedRoomVersionError, | ||
) | ||
from synapse.api.ratelimiting import Ratelimiter | ||
from synapse.events import EventBase | ||
from synapse.types import JsonDict | ||
from synapse.types import JsonDict, Requester | ||
from synapse.util.caches.response_cache import ResponseCache | ||
|
||
if TYPE_CHECKING: | ||
|
@@ -93,6 +94,9 @@ def __init__(self, hs: "HomeServer"): | |
self._event_serializer = hs.get_event_client_serializer() | ||
self._server_name = hs.hostname | ||
self._federation_client = hs.get_federation_client() | ||
self._ratelimiter = Ratelimiter( | ||
store=self._store, clock=hs.get_clock(), rate_hz=5, burst_count=10 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where did these numbers come from, out of interest? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I made them up, they should allow for 5 queries per second without enforcing any ratelimiting until 10 queries. This might be a bit high, but I'm never quite sure what to put here and usually err on the side of allowing more. 🤷 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fair enough. We can always tighten the limits! (Though there's a part of me that wonders if rate limiting ought to be handled by the load balancer to keep Synapse itself simpler.) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Some rate limits can be tweaked by the Synapse config, FWIW. They can also depend on particular data, e.g. you could limit the message rate per user, per room, per message type (or something like that). |
||
) | ||
|
||
# If a user tries to fetch the same page multiple times in quick succession, | ||
# only process the first attempt and return its result to subsequent requests. | ||
|
@@ -249,7 +253,7 @@ async def get_space_summary( | |
|
||
async def get_room_hierarchy( | ||
self, | ||
requester: str, | ||
requester: Requester, | ||
requested_room_id: str, | ||
suggested_only: bool = False, | ||
max_depth: Optional[int] = None, | ||
|
@@ -276,22 +280,24 @@ async def get_room_hierarchy( | |
Returns: | ||
The JSON hierarchy dictionary. | ||
""" | ||
await self._ratelimiter.ratelimit(requester) | ||
|
||
# If a user tries to fetch the same page multiple times in quick succession, | ||
# only process the first attempt and return its result to subsequent requests. | ||
# | ||
# This is due to the pagination process mutating internal state, attempting | ||
# to process multiple requests for the same page will result in errors. | ||
return await self._pagination_response_cache.wrap( | ||
( | ||
requester, | ||
requester.user.to_string(), | ||
requested_room_id, | ||
suggested_only, | ||
max_depth, | ||
limit, | ||
from_token, | ||
), | ||
self._get_room_hierarchy, | ||
requester, | ||
requester.user.to_string(), | ||
requested_room_id, | ||
suggested_only, | ||
max_depth, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this one get
v1
too in that case?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, this endpoint was not stabilized and will be removed in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool, thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No problem! It was still being hit fairly actively on matrix.org last time I checked unfortunately. It was essentially replaced with the
/hierarchy
version.#10946 tracks this work for cross-referencing.