mirror of
https://github.com/element-hq/synapse.git
synced 2026-11-04 06:57:20 +00:00
Port find_relevant_room_ids_for_extension to Rust
`rust/src/sliding_sync/extensions/relevant.rs` holds the matching of an extension's `lists` and `rooms` against the response. Phase 5 uses it, and it is cheap to port now. A list without exactly one `SYNC` operation fails, as Python's assertion does: that is a list with no ranges or several, under an extension that names it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L51DoxB8VnH8UjXJFDgpNG
This commit is contained in:
1 parent
d8a93ec3b8
commit
4f4611dc58
5 files changed
+222
No files matched your search
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* This file is licensed under the Affero General Public License (AGPL) version 3.
|
||||
*
|
||||
* Copyright (C) 2026 Element Creations Ltd.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* See the GNU Affero General Public License for more details:
|
||||
* <https://www.gnu.org/licenses/agpl-3.0.html>.
|
||||
*/
|
||||
|
||||
//! The sliding sync extensions (`synapse.handlers.sliding_sync.extensions`).
|
||||
|
||||
use pyo3::{prelude::*, types::PyModule};
|
||||
|
||||
pub mod relevant;
|
||||
|
||||
pub fn register_test_functions(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
relevant::register_test_functions(m)
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* This file is licensed under the Affero General Public License (AGPL) version 3.
|
||||
*
|
||||
* Copyright (C) 2026 Element Creations Ltd.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* See the GNU Affero General Public License for more details:
|
||||
* <https://www.gnu.org/licenses/agpl-3.0.html>.
|
||||
*/
|
||||
|
||||
//! `find_relevant_room_ids_for_extension`: the rooms an extension covers,
|
||||
//! from its `lists` and `rooms` and what the response holds.
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::{bail, Context};
|
||||
use indexmap::IndexMap;
|
||||
use pyo3::{exceptions::PyAssertionError, prelude::*, types::PyModule};
|
||||
|
||||
use crate::sliding_sync::room_lists::lists::SyncOp;
|
||||
|
||||
/// The response's lists, by name, each with its operations.
|
||||
pub type ActualLists = IndexMap<Arc<str>, Vec<SyncOp>>;
|
||||
|
||||
/// The rooms of a list's single `SYNC` operation. Python asserts there is
|
||||
/// exactly one, so a list with no ranges, or several, fails here.
|
||||
fn single_op_rooms<'a>(name: &str, ops: &'a [SyncOp]) -> anyhow::Result<&'a [Arc<str>]> {
|
||||
match ops {
|
||||
[op] => Ok(&op.room_ids),
|
||||
_ => bail!("list {name} has {} operations, not one", ops.len()),
|
||||
}
|
||||
}
|
||||
|
||||
/// `find_relevant_room_ids_for_extension`: of the response, the rooms
|
||||
/// `requested_room_ids` names among `actual_room_ids` (the room
|
||||
/// subscriptions), and the rooms of the lists `requested_lists` names. `*`
|
||||
/// means all of them.
|
||||
pub fn find_relevant_room_ids_for_extension(
|
||||
requested_lists: Option<&[String]>,
|
||||
requested_room_ids: Option<&[String]>,
|
||||
actual_lists: &ActualLists,
|
||||
actual_room_ids: &HashSet<Arc<str>>,
|
||||
) -> anyhow::Result<HashSet<Arc<str>>> {
|
||||
let mut relevant: HashSet<Arc<str>> = HashSet::new();
|
||||
|
||||
for room_id in requested_room_ids.unwrap_or_default() {
|
||||
if room_id == "*" {
|
||||
relevant.extend(actual_room_ids.iter().cloned());
|
||||
break;
|
||||
}
|
||||
if let Some(room_id) = actual_room_ids.get(room_id.as_str()) {
|
||||
relevant.insert(Arc::clone(room_id));
|
||||
}
|
||||
}
|
||||
|
||||
for list_key in requested_lists.unwrap_or_default() {
|
||||
if list_key == "*" {
|
||||
for (name, ops) in actual_lists {
|
||||
relevant.extend(single_op_rooms(name, ops)?.iter().cloned());
|
||||
}
|
||||
break;
|
||||
}
|
||||
if let Some(ops) = actual_lists.get(list_key.as_str()) {
|
||||
relevant.extend(
|
||||
single_op_rooms(list_key, ops)
|
||||
.context("an extension's list")?
|
||||
.iter()
|
||||
.cloned(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(relevant)
|
||||
}
|
||||
|
||||
/// `find_relevant_room_ids_for_extension` through the Rust implementation,
|
||||
/// each list as its operations' room IDs. Raises `AssertionError` as
|
||||
/// Python does.
|
||||
#[pyfunction]
|
||||
fn find_relevant_room_ids_for_test(
|
||||
requested_lists: Option<Vec<String>>,
|
||||
requested_room_ids: Option<Vec<String>>,
|
||||
actual_lists: Vec<(String, Vec<Vec<String>>)>,
|
||||
actual_room_ids: HashSet<String>,
|
||||
) -> PyResult<Vec<String>> {
|
||||
let actual_lists: ActualLists = actual_lists
|
||||
.into_iter()
|
||||
.map(|(name, ops)| {
|
||||
let ops = ops
|
||||
.into_iter()
|
||||
.map(|room_ids| SyncOp {
|
||||
range: (0, 0),
|
||||
room_ids: room_ids.into_iter().map(Arc::from).collect(),
|
||||
})
|
||||
.collect();
|
||||
(Arc::from(name), ops)
|
||||
})
|
||||
.collect();
|
||||
let actual_room_ids: HashSet<Arc<str>> = actual_room_ids.into_iter().map(Arc::from).collect();
|
||||
let relevant = find_relevant_room_ids_for_extension(
|
||||
requested_lists.as_deref(),
|
||||
requested_room_ids.as_deref(),
|
||||
&actual_lists,
|
||||
&actual_room_ids,
|
||||
)
|
||||
.map_err(|e| PyAssertionError::new_err(e.to_string()))?;
|
||||
let mut relevant: Vec<String> = relevant.iter().map(|r| r.to_string()).collect();
|
||||
relevant.sort();
|
||||
Ok(relevant)
|
||||
}
|
||||
|
||||
pub fn register_test_functions(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
m.add_function(wrap_pyfunction!(find_relevant_room_ids_for_test, m)?)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -38,6 +38,7 @@ use crate::storage::sync_store::{SyncDbStats, SyncStore};
|
||||
|
||||
pub mod config;
|
||||
pub mod connection;
|
||||
pub mod extensions;
|
||||
pub mod request;
|
||||
pub mod required_state;
|
||||
pub mod room;
|
||||
@@ -168,6 +169,7 @@ pub fn register_module(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()>
|
||||
child_module.add_class::<request::SlidingSyncRequest>()?;
|
||||
config::register_test_functions(&child_module)?;
|
||||
connection::register_test_functions(&child_module)?;
|
||||
extensions::register_test_functions(&child_module)?;
|
||||
required_state::register_test_functions(&child_module)?;
|
||||
room::register_test_functions(&child_module)?;
|
||||
room_lists::register_test_functions(&child_module)?;
|
||||
|
||||
@@ -240,3 +240,11 @@ def room_config_to_persist_for_test(
|
||||
ignore_timeline_bound: bool,
|
||||
limited: bool | None,
|
||||
) -> tuple[int, dict[str, list[str]]] | None: ...
|
||||
def find_relevant_room_ids_for_test(
|
||||
requested_lists: list[str] | None,
|
||||
requested_room_ids: list[str] | None,
|
||||
actual_lists: list[tuple[str, list[list[str]]]],
|
||||
actual_room_ids: set[str],
|
||||
) -> list[str]:
|
||||
"""Each list as its operations' room IDs. Raises `AssertionError` as
|
||||
Python does."""
|
||||
@@ -35,6 +35,7 @@ from synapse.handlers.sliding_sync import (
|
||||
_stripped_state,
|
||||
_timeline_bounds,
|
||||
)
|
||||
from synapse.handlers.sliding_sync.extensions import SlidingSyncExtensionHandler
|
||||
from synapse.handlers.sliding_sync.room_lists import (
|
||||
SlidingSyncRoomLists,
|
||||
_sync_list_ops,
|
||||
@@ -53,11 +54,13 @@ from synapse.types.handlers import SLIDING_SYNC_DEFAULT_BUMP_EVENT_TYPES
|
||||
from synapse.types.handlers.sliding_sync import (
|
||||
HaveSentRoom,
|
||||
HaveSentRoomFlag,
|
||||
OperationType,
|
||||
PerConnectionState,
|
||||
RoomLazyMembershipChanges,
|
||||
RoomStatusMap,
|
||||
RoomSyncConfig,
|
||||
SlidingSyncConfig,
|
||||
SlidingSyncResult,
|
||||
StateValues,
|
||||
)
|
||||
from synapse.types.state import StateFilter
|
||||
@@ -1210,3 +1213,69 @@ class RoomHelpersTestCase(unittest.TestCase):
|
||||
(python.timeline_limit, _as_sets(python.required_state_map)),
|
||||
context,
|
||||
)
|
||||
|
||||
|
||||
class RelevantRoomIdsTestCase(unittest.TestCase):
|
||||
"""`rust/src/sliding_sync/extensions/relevant.rs`"""
|
||||
|
||||
def test_against_python(self) -> None:
|
||||
rng = random.Random(18)
|
||||
names = ["a", "b", "c", "*"]
|
||||
raised = 0
|
||||
for _ in range(2000):
|
||||
actual_lists = [
|
||||
(
|
||||
name,
|
||||
[
|
||||
rng.sample(_ROOMS, rng.randint(0, 3))
|
||||
for _ in range(rng.choice([1, 1, 1, 1, 0, 2]))
|
||||
],
|
||||
)
|
||||
for name in rng.sample(names[:3], rng.randint(0, 3))
|
||||
]
|
||||
actual_room_ids = set(rng.sample(_ROOMS, rng.randint(0, 3)))
|
||||
requested_lists = rng.choice(
|
||||
[None, rng.sample(names + ["missing"], rng.randint(0, 3))]
|
||||
)
|
||||
requested_room_ids = rng.choice(
|
||||
[None, rng.sample([*_ROOMS[:4], "*"], rng.randint(0, 3))]
|
||||
)
|
||||
|
||||
try:
|
||||
python: list[str] | None = sorted(
|
||||
SlidingSyncExtensionHandler.find_relevant_room_ids_for_extension(
|
||||
cast(SlidingSyncExtensionHandler, None),
|
||||
requested_lists,
|
||||
requested_room_ids,
|
||||
{
|
||||
name: SlidingSyncResult.SlidingWindowList(
|
||||
count=0,
|
||||
ops=[
|
||||
SlidingSyncResult.SlidingWindowList.Operation(
|
||||
op=OperationType.SYNC,
|
||||
range=(0, 0),
|
||||
room_ids=room_ids,
|
||||
)
|
||||
for room_ids in ops
|
||||
],
|
||||
)
|
||||
for name, ops in actual_lists
|
||||
},
|
||||
actual_room_ids,
|
||||
)
|
||||
)
|
||||
except AssertionError:
|
||||
python = None
|
||||
raised += 1
|
||||
try:
|
||||
result: list[str] | None = rust.find_relevant_room_ids_for_test(
|
||||
requested_lists, requested_room_ids, actual_lists, actual_room_ids
|
||||
)
|
||||
except AssertionError:
|
||||
result = None
|
||||
self.assertEqual(
|
||||
result,
|
||||
python,
|
||||
(requested_lists, requested_room_ids, actual_lists, actual_room_ids),
|
||||
)
|
||||
self.assertGreater(raised, 0)
|
||||
Reference in new issue
Block a user