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

Commit

Permalink
Make pagination of rooms in admin api stable (#11737)
Browse files Browse the repository at this point in the history
Always add state.room_id after the configurable ORDER BY. Otherwise,
for any sort, certain pages can contain results from
other pages. (Especially when sorting by creator, since there may
be many rooms by the same creator)

* Document different order direction of numerical fields

"joined_members", "joined_local_members", "version" and "state_events"
are ordered in descending direction by default (dir=f). Added a note
in tests to explain the differences in ordering.

Signed-off-by: Daniël Sonck <daniel@sonck.nl>
  • Loading branch information
dsonck92 committed Jan 17, 2022
1 parent e7da1ce commit 6b241f5
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 28 deletions.
1 change: 1 addition & 0 deletions changelog.d/11737.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Make the list rooms admin api sort stable. Contributed by Daniël Sonck.
18 changes: 9 additions & 9 deletions synapse/storage/databases/main/room.py
Original file line number Diff line number Diff line change
Expand Up @@ -551,24 +551,24 @@ async def get_rooms_paginate(
FROM room_stats_state state
INNER JOIN room_stats_current curr USING (room_id)
INNER JOIN rooms USING (room_id)
%s
ORDER BY %s %s
{where}
ORDER BY {order_by} {direction}, state.room_id {direction}
LIMIT ?
OFFSET ?
""" % (
where_statement,
order_by_column,
"ASC" if order_by_asc else "DESC",
""".format(
where=where_statement,
order_by=order_by_column,
direction="ASC" if order_by_asc else "DESC",
)

# Use a nested SELECT statement as SQL can't count(*) with an OFFSET
count_sql = """
SELECT count(*) FROM (
SELECT room_id FROM room_stats_state state
%s
{where}
) AS get_room_ids
""" % (
where_statement,
""".format(
where=where_statement,
)

def _get_rooms_paginate_txn(
Expand Down
47 changes: 28 additions & 19 deletions tests/rest/admin/test_room.py
Original file line number Diff line number Diff line change
Expand Up @@ -1089,6 +1089,8 @@ def test_list_rooms(self) -> None:
)
room_ids.append(room_id)

room_ids.sort()

# Request the list of rooms
url = "/_synapse/admin/v1/rooms"
channel = self.make_request(
Expand Down Expand Up @@ -1360,6 +1362,12 @@ def _order_test(
room_id_2 = self.helper.create_room_as(self.admin_user, tok=self.admin_user_tok)
room_id_3 = self.helper.create_room_as(self.admin_user, tok=self.admin_user_tok)

# Also create a list sorted by IDs for properties that are equal (and thus sorted by room_id)
sorted_by_room_id_asc = [room_id_1, room_id_2, room_id_3]
sorted_by_room_id_asc.sort()
sorted_by_room_id_desc = sorted_by_room_id_asc.copy()
sorted_by_room_id_desc.reverse()

# Set room names in alphabetical order. room 1 -> A, 2 -> B, 3 -> C
self.helper.send_state(
room_id_1,
Expand Down Expand Up @@ -1405,41 +1413,42 @@ def _order_test(
_order_test("canonical_alias", [room_id_1, room_id_2, room_id_3])
_order_test("canonical_alias", [room_id_3, room_id_2, room_id_1], reverse=True)

# Note: joined_member counts are sorted in descending order when dir=f
_order_test("joined_members", [room_id_3, room_id_2, room_id_1])
_order_test("joined_members", [room_id_1, room_id_2, room_id_3], reverse=True)

# Note: joined_local_member counts are sorted in descending order when dir=f
_order_test("joined_local_members", [room_id_3, room_id_2, room_id_1])
_order_test(
"joined_local_members", [room_id_1, room_id_2, room_id_3], reverse=True
)

_order_test("version", [room_id_1, room_id_2, room_id_3])
_order_test("version", [room_id_1, room_id_2, room_id_3], reverse=True)
# Note: versions are sorted in descending order when dir=f
_order_test("version", sorted_by_room_id_asc, reverse=True)
_order_test("version", sorted_by_room_id_desc)

_order_test("creator", [room_id_1, room_id_2, room_id_3])
_order_test("creator", [room_id_1, room_id_2, room_id_3], reverse=True)
_order_test("creator", sorted_by_room_id_asc)
_order_test("creator", sorted_by_room_id_desc, reverse=True)

_order_test("encryption", [room_id_1, room_id_2, room_id_3])
_order_test("encryption", [room_id_1, room_id_2, room_id_3], reverse=True)
_order_test("encryption", sorted_by_room_id_asc)
_order_test("encryption", sorted_by_room_id_desc, reverse=True)

_order_test("federatable", [room_id_1, room_id_2, room_id_3])
_order_test("federatable", [room_id_1, room_id_2, room_id_3], reverse=True)
_order_test("federatable", sorted_by_room_id_asc)
_order_test("federatable", sorted_by_room_id_desc, reverse=True)

_order_test("public", [room_id_1, room_id_2, room_id_3])
# Different sort order of SQlite and PostreSQL
# _order_test("public", [room_id_3, room_id_2, room_id_1], reverse=True)
_order_test("public", sorted_by_room_id_asc)
_order_test("public", sorted_by_room_id_desc, reverse=True)

_order_test("join_rules", [room_id_1, room_id_2, room_id_3])
_order_test("join_rules", [room_id_1, room_id_2, room_id_3], reverse=True)
_order_test("join_rules", sorted_by_room_id_asc)
_order_test("join_rules", sorted_by_room_id_desc, reverse=True)

_order_test("guest_access", [room_id_1, room_id_2, room_id_3])
_order_test("guest_access", [room_id_1, room_id_2, room_id_3], reverse=True)
_order_test("guest_access", sorted_by_room_id_asc)
_order_test("guest_access", sorted_by_room_id_desc, reverse=True)

_order_test("history_visibility", [room_id_1, room_id_2, room_id_3])
_order_test(
"history_visibility", [room_id_1, room_id_2, room_id_3], reverse=True
)
_order_test("history_visibility", sorted_by_room_id_asc)
_order_test("history_visibility", sorted_by_room_id_desc, reverse=True)

# Note: state_event counts are sorted in descending order when dir=f
_order_test("state_events", [room_id_3, room_id_2, room_id_1])
_order_test("state_events", [room_id_1, room_id_2, room_id_3], reverse=True)

Expand Down

0 comments on commit 6b241f5

Please sign in to comment.