(syncv5 / snake sync) Invitees fail to receive room invitations in a timely manner or even fail to receive them at all #714

Open
opened 2025-03-18 00:53:27 +00:00 by blakeavato · 2 comments
blakeavato commented 2025-03-18 00:53:27 +00:00 (Migrated from github.com)

The root cause of this issue is that the newly invited rooms are not returned to the client in the sync request.
As a temporary workaround, I've made the following modifications in src/api/client/sync/v5.rs:
add:

			if i == 0 && list_id == "all_rooms" && !all_invited_rooms.is_empty() {
				let room_ids_set: BTreeSet<&RoomId> = room_ids.iter().copied().collect();
				let additional_invites: Vec<&RoomId> = all_invited_rooms
					.iter()
					.filter(|&room_id| !room_ids_set.contains(room_id))
					.copied()
					.collect();

				if !additional_invites.is_empty() {
					tracing::info!(
						"Appending {} new invite rooms to room_ids",
						additional_invites.len()
					);
					room_ids.extend(additional_invites);
				}
			}

in function:

async fn handle_lists<'a>(
	services: crate::State,
	(sender_user, sender_device, globalsince, body): SyncInfo<'_>,
	all_invited_rooms: &Vec<&'a RoomId>,
	all_joined_rooms: &Vec<&'a RoomId>,
	all_rooms: &Vec<&'a RoomId>,
	todo_rooms: &'a mut TodoRooms,
	known_rooms: &'a KnownRooms,
	response: &'_ mut sync_events::v5::Response,
) -> KnownRooms {
	for (list_id, list) in &body.lists {
		let active_rooms = match list.filters.clone().and_then(|f| f.is_invite) {
			| Some(true) => all_invited_rooms,
			| Some(false) => all_joined_rooms,
			| None => all_rooms,
		};
		let active_rooms = match list.filters.clone().map(|f| f.not_room_types) {
			| Some(filter) if filter.is_empty() => active_rooms,
			| Some(value) => &filter_rooms(&services, active_rooms, &value, true).await,
			| None => active_rooms,
		};

		let mut new_known_rooms: BTreeSet<OwnedRoomId> = BTreeSet::new();
		let ranges = list.ranges.clone();
		for (i, mut range) in ranges.into_iter().enumerate() {
			range.0 = uint!(0);
			range.1 = range
				.1
				.clamp(range.0, UInt::try_from(active_rooms.len()).unwrap_or(UInt::MAX));

			let mut room_ids =
				active_rooms[usize_from_ruma(range.0)..usize_from_ruma(range.1)].to_vec();

			if i == 0 && list_id == "all_rooms" && !all_invited_rooms.is_empty() {
				let room_ids_set: BTreeSet<&RoomId> = room_ids.iter().copied().collect();
				let additional_invites: Vec<&RoomId> = all_invited_rooms
					.iter()
					.filter(|&room_id| !room_ids_set.contains(room_id))
					.copied()
					.collect();

				if !additional_invites.is_empty() {
					tracing::info!(
						"Appending {} new invite rooms to room_ids",
						additional_invites.len()
					);
					room_ids.extend(additional_invites);
				}
			}

			let new_rooms: BTreeSet<OwnedRoomId> =
				room_ids.clone().into_iter().map(From::from).collect();
			new_known_rooms.extend(new_rooms);

			for room_id in room_ids {
				let todo_room = todo_rooms.entry(room_id.to_owned()).or_insert((
					BTreeSet::new(),
					0_usize,
					u64::MAX,
				));

				let limit: usize = usize_from_ruma(list.room_details.timeline_limit).min(100);

				todo_room.0.extend(
					list.room_details
						.required_state
						.iter()
						.map(|(ty, sk)| (ty.clone(), sk.as_str().into())),
				);

				todo_room.1 = todo_room.1.max(limit);
				// 0 means unknown because it got out of date
				todo_room.2 = todo_room.2.min(
					known_rooms
						.get(list_id.as_str())
						.and_then(|k| k.get(room_id))
						.copied()
						.unwrap_or(0),
				);
			}
		}
		response
			.lists
			.insert(list_id.clone(), sync_events::v5::response::List {
				count: ruma_from_usize(active_rooms.len()),
			});

		if let Some(conn_id) = &body.conn_id {
			services.sync.update_snake_sync_known_rooms(
				sender_user,
				sender_device,
				conn_id.clone(),
				list_id.clone(),
				new_known_rooms,
				globalsince,
			);
		}
	}
	BTreeMap::default()
}
The root cause of this issue is that the newly invited rooms are not returned to the client in the sync request. As a temporary workaround, I've made the following modifications in src/api/client/sync/v5.rs: add: ```rust if i == 0 && list_id == "all_rooms" && !all_invited_rooms.is_empty() { let room_ids_set: BTreeSet<&RoomId> = room_ids.iter().copied().collect(); let additional_invites: Vec<&RoomId> = all_invited_rooms .iter() .filter(|&room_id| !room_ids_set.contains(room_id)) .copied() .collect(); if !additional_invites.is_empty() { tracing::info!( "Appending {} new invite rooms to room_ids", additional_invites.len() ); room_ids.extend(additional_invites); } } ``` in function: ```rust async fn handle_lists<'a>( services: crate::State, (sender_user, sender_device, globalsince, body): SyncInfo<'_>, all_invited_rooms: &Vec<&'a RoomId>, all_joined_rooms: &Vec<&'a RoomId>, all_rooms: &Vec<&'a RoomId>, todo_rooms: &'a mut TodoRooms, known_rooms: &'a KnownRooms, response: &'_ mut sync_events::v5::Response, ) -> KnownRooms { for (list_id, list) in &body.lists { let active_rooms = match list.filters.clone().and_then(|f| f.is_invite) { | Some(true) => all_invited_rooms, | Some(false) => all_joined_rooms, | None => all_rooms, }; let active_rooms = match list.filters.clone().map(|f| f.not_room_types) { | Some(filter) if filter.is_empty() => active_rooms, | Some(value) => &filter_rooms(&services, active_rooms, &value, true).await, | None => active_rooms, }; let mut new_known_rooms: BTreeSet<OwnedRoomId> = BTreeSet::new(); let ranges = list.ranges.clone(); for (i, mut range) in ranges.into_iter().enumerate() { range.0 = uint!(0); range.1 = range .1 .clamp(range.0, UInt::try_from(active_rooms.len()).unwrap_or(UInt::MAX)); let mut room_ids = active_rooms[usize_from_ruma(range.0)..usize_from_ruma(range.1)].to_vec(); if i == 0 && list_id == "all_rooms" && !all_invited_rooms.is_empty() { let room_ids_set: BTreeSet<&RoomId> = room_ids.iter().copied().collect(); let additional_invites: Vec<&RoomId> = all_invited_rooms .iter() .filter(|&room_id| !room_ids_set.contains(room_id)) .copied() .collect(); if !additional_invites.is_empty() { tracing::info!( "Appending {} new invite rooms to room_ids", additional_invites.len() ); room_ids.extend(additional_invites); } } let new_rooms: BTreeSet<OwnedRoomId> = room_ids.clone().into_iter().map(From::from).collect(); new_known_rooms.extend(new_rooms); for room_id in room_ids { let todo_room = todo_rooms.entry(room_id.to_owned()).or_insert(( BTreeSet::new(), 0_usize, u64::MAX, )); let limit: usize = usize_from_ruma(list.room_details.timeline_limit).min(100); todo_room.0.extend( list.room_details .required_state .iter() .map(|(ty, sk)| (ty.clone(), sk.as_str().into())), ); todo_room.1 = todo_room.1.max(limit); // 0 means unknown because it got out of date todo_room.2 = todo_room.2.min( known_rooms .get(list_id.as_str()) .and_then(|k| k.get(room_id)) .copied() .unwrap_or(0), ); } } response .lists .insert(list_id.clone(), sync_events::v5::response::List { count: ruma_from_usize(active_rooms.len()), }); if let Some(conn_id) = &body.conn_id { services.sync.update_snake_sync_known_rooms( sender_user, sender_device, conn_id.clone(), list_id.clone(), new_known_rooms, globalsince, ); } } BTreeMap::default() } ```
girlbossceo commented 2025-03-18 01:02:03 +00:00 (Migrated from github.com)

Are you able to clarify if this is an issue with simplified sliding sync (syncv5), used by Element X, or normal syncv3 used by Element Web / etc?

Are you able to clarify if this is an issue with simplified sliding sync (syncv5), used by Element X, or normal syncv3 used by Element Web / etc?
blakeavato commented 2025-03-18 01:05:14 +00:00 (Migrated from github.com)

My client is elementx, and I think there are issues with the implementation of syncv5.
After modification, the client can receive invitations very quickly and can send and receive messages normally. There is no problem with multiple invitations/exit to the room.

My client is elementx, and I think there are issues with the implementation of syncv5. After modification, the client can receive invitations very quickly and can send and receive messages normally. There is no problem with multiple invitations/exit to the room.
This discussion has been locked. Commenting is limited to contributors.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
continuwuation/continuwuity#714
No description provided.