implement room.timeline.(not_)rooms filter on /sync

I asked in #matrix-spec:matrix.org and go clarification that we should be
omitting the timeline field completely for rooms that are filtered out
by the timeline.(not_)rooms filter. Ruma's skip_serializing_if attribute
on the timeline field will currently cause it to be omitted when events is
empty. If [this fix][1] is merged, it will be omitted only when events is
empty, prev_batch is None, and limited is false.

[1]: https://github.com/ruma/ruma/pull/1796

TODO: maybe do something about clippy::too_many_arguments
This commit is contained in:
Benjamin Lee 2024-05-03 15:56:27 -07:00
parent 458d6842fb
commit c48abf9f13
No known key found for this signature in database
GPG key ID: FB9624E2885D55A4
2 changed files with 37 additions and 12 deletions

View file

@ -170,6 +170,7 @@ pub(crate) async fn sync_events_route(
lazy_load_enabled,
lazy_load_send_redundant,
full_state,
&compiled_filter,
&mut device_list_updates,
&mut left_encrypted_users,
)
@ -207,6 +208,7 @@ pub(crate) async fn sync_events_route(
&next_batch_string,
full_state,
lazy_load_enabled,
&compiled_filter,
)
.await?;
}
@ -382,9 +384,11 @@ async fn load_joined_room(
lazy_load_enabled: bool,
lazy_load_send_redundant: bool,
full_state: bool,
filter: &CompiledFilterDefinition<'_>,
device_list_updates: &mut HashSet<OwnedUserId>,
left_encrypted_users: &mut HashSet<OwnedUserId>,
) -> Result<JoinedRoom> {
// TODO: can we skip this when the room is filtered out?
{
// Get and drop the lock to wait for remaining operations to finish
// This will make sure the we have all events until next_batch
@ -402,7 +406,7 @@ async fn load_joined_room(
}
let (timeline_pdus, limited) =
load_timeline(sender_user, room_id, sincecount, 10)?;
load_timeline(sender_user, room_id, sincecount, 10, Some(filter))?;
let send_notification_counts = !timeline_pdus.is_empty()
|| services()
@ -974,6 +978,7 @@ async fn load_joined_room(
room_id = %room_id,
),
)]
#[allow(clippy::too_many_arguments)]
async fn handle_left_room(
room_id: OwnedRoomId,
sender_user: &UserId,
@ -982,6 +987,7 @@ async fn handle_left_room(
next_batch_string: &str,
full_state: bool,
lazy_load_enabled: bool,
filter: &CompiledFilterDefinition<'_>,
) -> Result<()> {
{
// Get and drop the lock to wait for remaining operations to finish
@ -1006,6 +1012,20 @@ async fn handle_left_room(
return Ok(());
}
let timeline = if filter.room.timeline.room_allowed(&room_id) {
Timeline {
limited: false,
prev_batch: Some(next_batch_string.to_owned()),
events: vec![],
}
} else {
Timeline {
limited: false,
prev_batch: None,
events: vec![],
}
};
if !services().rooms.metadata.exists(&room_id)? {
// This is just a rejected invite, not a room we know
let event = PduEvent {
@ -1037,11 +1057,7 @@ async fn handle_left_room(
account_data: RoomAccountData {
events: Vec::new(),
},
timeline: Timeline {
limited: false,
prev_batch: Some(next_batch_string.to_owned()),
events: Vec::new(),
},
timeline,
state: State {
events: vec![event.to_sync_state_event()],
},
@ -1126,11 +1142,7 @@ async fn handle_left_room(
account_data: RoomAccountData {
events: Vec::new(),
},
timeline: Timeline {
limited: false,
prev_batch: Some(next_batch_string.to_owned()),
events: Vec::new(),
},
timeline,
state: State {
events: left_state_events,
},
@ -1145,9 +1157,17 @@ fn load_timeline(
room_id: &RoomId,
roomsincecount: PduCount,
limit: u64,
filter: Option<&CompiledFilterDefinition<'_>>,
) -> Result<(Vec<(PduCount, PduEvent)>, bool), Error> {
let timeline_pdus;
let limited;
if let Some(filter) = filter {
if !filter.room.timeline.room_allowed(room_id) {
return Ok((vec![], false));
}
}
if services().rooms.timeline.last_timeline_count(sender_user, room_id)?
> roomsincecount
{
@ -1622,6 +1642,7 @@ pub(crate) async fn sync_events_v4_route(
room_id,
roomsincecount,
*timeline_limit,
None,
)?;
if roomsince != &0 && timeline_pdus.is_empty() {

View file

@ -12,7 +12,9 @@
//! In `/messages`, if the room is rejected by the filter, we can skip the
//! entire request. The outer loop of our `/sync` implementation is over rooms,
//! and so we are able to skip work for an entire room if it is rejected by the
//! top-level `filter.rooms.room`.
//! top-level `filter.rooms.room`. Similarly, when a room is rejected for all
//! events in a particular category, we can skip work generating events in that
//! category for the rejected room.
use std::{collections::HashSet, hash::Hash};
@ -156,6 +158,7 @@ pub(crate) struct CompiledFilterDefinition<'a> {
pub(crate) struct CompiledRoomFilter<'a> {
rooms: AllowDenyList<'a, RoomId>,
pub(crate) timeline: CompiledRoomEventFilter<'a>,
}
pub(crate) struct CompiledRoomEventFilter<'a> {
@ -193,6 +196,7 @@ impl<'a> TryFrom<&'a RoomFilter> for CompiledRoomFilter<'a> {
source.rooms.as_deref(),
&source.not_rooms,
),
timeline: (&source.timeline).try_into()?,
})
}
}