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

This commit is contained in:
Benjamin Lee 2024-05-03 17:09:19 -07:00
parent c48abf9f13
commit e6f2b6c9ad
No known key found for this signature in database
GPG key ID: FB9624E2885D55A4
2 changed files with 177 additions and 155 deletions

View file

@ -443,6 +443,8 @@ async fn load_joined_room(
let since_shortstatehash = let since_shortstatehash =
services().rooms.user.get_token_shortstatehash(room_id, since)?; services().rooms.user.get_token_shortstatehash(room_id, since)?;
let skip_state_events = !filter.room.state.room_allowed(room_id);
let ( let (
heroes, heroes,
joined_member_count, joined_member_count,
@ -583,47 +585,51 @@ async fn load_joined_room(
let mut state_events = Vec::new(); let mut state_events = Vec::new();
let mut lazy_loaded = HashSet::new(); let mut lazy_loaded = HashSet::new();
let mut i = 0; if !skip_state_events {
for (shortstatekey, id) in current_state_ids { let mut i = 0;
let (event_type, state_key) = services() for (shortstatekey, id) in current_state_ids {
.rooms let (event_type, state_key) = services()
.short .rooms
.get_statekey_from_short(shortstatekey)?; .short
.get_statekey_from_short(shortstatekey)?;
if event_type != StateEventType::RoomMember { if event_type != StateEventType::RoomMember {
let Some(pdu) = services().rooms.timeline.get_pdu(&id)? let Some(pdu) =
else { services().rooms.timeline.get_pdu(&id)?
error!("Pdu in state not found: {}", id); else {
continue; error!("Pdu in state not found: {}", id);
}; continue;
state_events.push(pdu); };
state_events.push(pdu);
i += 1; i += 1;
if i % 100 == 0 { if i % 100 == 0 {
tokio::task::yield_now().await; tokio::task::yield_now().await;
} }
} else if !lazy_load_enabled } else if !lazy_load_enabled
|| full_state || full_state
|| timeline_users.contains(&state_key) || timeline_users.contains(&state_key)
// TODO: Delete the following line when this is resolved: https://github.com/vector-im/element-web/issues/22565 // TODO: Delete the following line when this is resolved: https://github.com/vector-im/element-web/issues/22565
|| *sender_user == state_key || *sender_user == state_key
{ {
let Some(pdu) = services().rooms.timeline.get_pdu(&id)? let Some(pdu) =
else { services().rooms.timeline.get_pdu(&id)?
error!("Pdu in state not found: {}", id); else {
continue; error!("Pdu in state not found: {}", id);
}; continue;
};
// This check is in case a bad user ID made it into the // This check is in case a bad user ID made it into the
// database // database
if let Ok(uid) = UserId::parse(&state_key) { if let Ok(uid) = UserId::parse(&state_key) {
lazy_loaded.insert(uid); lazy_loaded.insert(uid);
} }
state_events.push(pdu); state_events.push(pdu);
i += 1; i += 1;
if i % 100 == 0 { if i % 100 == 0 {
tokio::task::yield_now().await; tokio::task::yield_now().await;
}
} }
} }
} }
@ -792,66 +798,80 @@ async fn load_joined_room(
(None, None, Vec::new()) (None, None, Vec::new())
}; };
let mut state_events = delta_state_events; let state_events = if skip_state_events {
let mut lazy_loaded = HashSet::new(); vec![]
} else {
let mut state_events = delta_state_events;
let mut lazy_loaded = HashSet::new();
// Mark all member events we're returning as lazy-loaded // Mark all member events we're returning as lazy-loaded
for pdu in &state_events { for pdu in &state_events {
if pdu.kind == TimelineEventType::RoomMember { if pdu.kind == TimelineEventType::RoomMember {
match UserId::parse( match UserId::parse(
pdu.state_key pdu.state_key
.as_ref() .as_ref()
.expect("State event has state key") .expect("State event has state key")
.clone(), .clone(),
) { ) {
Ok(state_key_userid) => { Ok(state_key_userid) => {
lazy_loaded.insert(state_key_userid); lazy_loaded.insert(state_key_userid);
} }
Err(e) => { Err(e) => {
error!("Invalid state key for member event: {}", e); error!(
"Invalid state key for member event: {}",
e
);
}
} }
} }
} }
}
// Fetch contextual member state events for events from the // Fetch contextual member state events for events from the
// timeline, and mark them as lazy-loaded as well. // timeline, and mark them as lazy-loaded as
for (_, event) in &timeline_pdus { // well.
if lazy_loaded.contains(&event.sender) { for (_, event) in &timeline_pdus {
continue; if lazy_loaded.contains(&event.sender) {
} continue;
}
if !services().rooms.lazy_loading.lazy_load_was_sent_before( if !services()
sender_user, .rooms
sender_device, .lazy_loading
room_id, .lazy_load_was_sent_before(
&event.sender, sender_user,
)? || lazy_load_send_redundant sender_device,
{
if let Some(member_event) =
services().rooms.state_accessor.room_state_get(
room_id, room_id,
&StateEventType::RoomMember, &event.sender,
event.sender.as_str(),
)? )?
|| lazy_load_send_redundant
{ {
lazy_loaded.insert(event.sender.clone()); if let Some(member_event) =
state_events.push(member_event); services().rooms.state_accessor.room_state_get(
room_id,
&StateEventType::RoomMember,
event.sender.as_str(),
)?
{
lazy_loaded.insert(event.sender.clone());
state_events.push(member_event);
}
} }
} }
}
services() services()
.rooms .rooms
.lazy_loading .lazy_loading
.lazy_load_mark_sent( .lazy_load_mark_sent(
sender_user, sender_user,
sender_device, sender_device,
room_id, room_id,
lazy_loaded, lazy_loaded,
next_batchcount, next_batchcount,
) )
.await; .await;
state_events
};
( (
heroes, heroes,
@ -1026,7 +1046,11 @@ async fn handle_left_room(
} }
}; };
if !services().rooms.metadata.exists(&room_id)? { let state = if !filter.room.state.room_allowed(&room_id) {
State {
events: vec![],
}
} else if !services().rooms.metadata.exists(&room_id)? {
// This is just a rejected invite, not a room we know // This is just a rejected invite, not a room we know
let event = PduEvent { let event = PduEvent {
event_id: EventId::new(services().globals.server_name()).into(), event_id: EventId::new(services().globals.server_name()).into(),
@ -1051,90 +1075,88 @@ async fn handle_left_room(
signatures: None, signatures: None,
}; };
left_rooms.insert( State {
room_id, events: vec![event.to_sync_state_event()],
LeftRoom { }
account_data: RoomAccountData { } else {
events: Vec::new(), let mut left_state_events = Vec::new();
},
timeline,
state: State {
events: vec![event.to_sync_state_event()],
},
},
);
return Ok(()); let since_shortstatehash =
} services().rooms.user.get_token_shortstatehash(&room_id, since)?;
let mut left_state_events = Vec::new(); let since_state_ids = match since_shortstatehash {
Some(s) => {
services().rooms.state_accessor.state_full_ids(s).await?
}
None => HashMap::new(),
};
let since_shortstatehash = let Some(left_event_id) =
services().rooms.user.get_token_shortstatehash(&room_id, since)?; services().rooms.state_accessor.room_state_get_id(
&room_id,
&StateEventType::RoomMember,
sender_user.as_str(),
)?
else {
error!("Left room but no left state event");
return Ok(());
};
let since_state_ids = match since_shortstatehash { let Some(left_shortstatehash) = services()
Some(s) => services().rooms.state_accessor.state_full_ids(s).await?, .rooms
None => HashMap::new(), .state_accessor
}; .pdu_shortstatehash(&left_event_id)?
else {
error!("Leave event has no state");
return Ok(());
};
let Some(left_event_id) = let mut left_state_ids = services()
services().rooms.state_accessor.room_state_get_id( .rooms
&room_id, .state_accessor
&StateEventType::RoomMember, .state_full_ids(left_shortstatehash)
sender_user.as_str(), .await?;
)?
else {
error!("Left room but no left state event");
return Ok(());
};
let Some(left_shortstatehash) = let leave_shortstatekey =
services().rooms.state_accessor.pdu_shortstatehash(&left_event_id)? services().rooms.short.get_or_create_shortstatekey(
else { &StateEventType::RoomMember,
error!("Leave event has no state"); sender_user.as_str(),
return Ok(()); )?;
};
let mut left_state_ids = services() left_state_ids.insert(leave_shortstatekey, left_event_id);
.rooms
.state_accessor
.state_full_ids(left_shortstatehash)
.await?;
let leave_shortstatekey = let mut i = 0;
services().rooms.short.get_or_create_shortstatekey( for (key, id) in left_state_ids {
&StateEventType::RoomMember, if full_state || since_state_ids.get(&key) != Some(&id) {
sender_user.as_str(), let (event_type, state_key) =
)?; services().rooms.short.get_statekey_from_short(key)?;
left_state_ids.insert(leave_shortstatekey, left_event_id); if !lazy_load_enabled
|| event_type != StateEventType::RoomMember
|| full_state
// TODO: Delete the following line when this is resolved: https://github.com/vector-im/element-web/issues/22565
|| *sender_user == state_key
{
let Some(pdu) = services().rooms.timeline.get_pdu(&id)?
else {
error!("Pdu in state not found: {}", id);
continue;
};
let mut i = 0; left_state_events.push(pdu.to_sync_state_event());
for (key, id) in left_state_ids {
if full_state || since_state_ids.get(&key) != Some(&id) {
let (event_type, state_key) =
services().rooms.short.get_statekey_from_short(key)?;
if !lazy_load_enabled i += 1;
|| event_type != StateEventType::RoomMember if i % 100 == 0 {
|| full_state tokio::task::yield_now().await;
// TODO: Delete the following line when this is resolved: https://github.com/vector-im/element-web/issues/22565 }
|| *sender_user == state_key
{
let Some(pdu) = services().rooms.timeline.get_pdu(&id)? else {
error!("Pdu in state not found: {}", id);
continue;
};
left_state_events.push(pdu.to_sync_state_event());
i += 1;
if i % 100 == 0 {
tokio::task::yield_now().await;
} }
} }
} }
}
State {
events: left_state_events,
}
};
left_rooms.insert( left_rooms.insert(
room_id.clone(), room_id.clone(),
@ -1143,9 +1165,7 @@ async fn handle_left_room(
events: Vec::new(), events: Vec::new(),
}, },
timeline, timeline,
state: State { state,
events: left_state_events,
},
}, },
); );

View file

@ -159,6 +159,7 @@ pub(crate) struct CompiledFilterDefinition<'a> {
pub(crate) struct CompiledRoomFilter<'a> { pub(crate) struct CompiledRoomFilter<'a> {
rooms: AllowDenyList<'a, RoomId>, rooms: AllowDenyList<'a, RoomId>,
pub(crate) timeline: CompiledRoomEventFilter<'a>, pub(crate) timeline: CompiledRoomEventFilter<'a>,
pub(crate) state: CompiledRoomEventFilter<'a>,
} }
pub(crate) struct CompiledRoomEventFilter<'a> { pub(crate) struct CompiledRoomEventFilter<'a> {
@ -197,6 +198,7 @@ impl<'a> TryFrom<&'a RoomFilter> for CompiledRoomFilter<'a> {
&source.not_rooms, &source.not_rooms,
), ),
timeline: (&source.timeline).try_into()?, timeline: (&source.timeline).try_into()?,
state: (&source.state).try_into()?,
}) })
} }
} }