fix: Correctly update space children on upgrade

This commit is contained in:
timedout
2026-05-25 19:37:29 +01:00
parent 5b8799e71f
commit 416814094c
+83 -15
View File
@@ -2,7 +2,9 @@ use std::cmp::max;
use axum::extract::State; use axum::extract::State;
use conduwuit::{ use conduwuit::{
Err, Error, Event, Result, err, Err, Error, Event, Result, debug,
debug::DebugInspect,
err, error,
info::room_version::UNSTABLE_ROOM_VERSIONS, info::room_version::UNSTABLE_ROOM_VERSIONS,
matrix::{StateKey, pdu::PartialPdu}, matrix::{StateKey, pdu::PartialPdu},
}; };
@@ -29,7 +31,7 @@ use serde_json::value::to_raw_value;
use crate::router::Ruma; use crate::router::Ruma;
/// Recommended transferable state events list from the spec /// Recommended transferable state events list from the spec
const TRANSFERABLE_STATE_EVENTS: &[StateEventType; 11] = &[ const TRANSFERABLE_STATE_EVENTS: &[StateEventType; 9] = &[
StateEventType::RoomServerAcl, StateEventType::RoomServerAcl,
StateEventType::RoomEncryption, StateEventType::RoomEncryption,
StateEventType::RoomName, StateEventType::RoomName,
@@ -39,9 +41,6 @@ const TRANSFERABLE_STATE_EVENTS: &[StateEventType; 11] = &[
StateEventType::RoomHistoryVisibility, StateEventType::RoomHistoryVisibility,
StateEventType::RoomJoinRules, StateEventType::RoomJoinRules,
StateEventType::RoomPowerLevels, StateEventType::RoomPowerLevels,
// MSC4168: https://github.com/matrix-org/matrix-spec-proposals/pull/4168
StateEventType::SpaceChild,
StateEventType::SpaceParent,
]; ];
/// Updates spaces that are marked as parents of old_room_id to instead point to /// Updates spaces that are marked as parents of old_room_id to instead point to
@@ -61,7 +60,8 @@ async fn msc4168_update_parent_spaces(
.rooms .rooms
.state_accessor .state_accessor
.room_state_keys(old_room_id, &StateEventType::SpaceParent) .room_state_keys(old_room_id, &StateEventType::SpaceParent)
.await?; .await
.debug_inspect(|k| debug!(?old_room_id, "Parents: {k:?}"))?;
for raw_parent_id in parents { for raw_parent_id in parents {
let parent_id = RoomId::parse(&raw_parent_id)?; let parent_id = RoomId::parse(&raw_parent_id)?;
@@ -77,6 +77,15 @@ async fn msc4168_update_parent_spaces(
old_room_id.as_str(), old_room_id.as_str(),
) )
.await .await
.debug_inspect_err(|e| {
error!(
?parent_id,
old_room_id=?old_room_id,
new_room_id=?new_room_id,
%e,
"failed to fetch m.space.child from parent"
)
})
else { else {
// If the space does not have a child event for this room, we can skip it // If the space does not have a child event for this room, we can skip it
continue; continue;
@@ -105,6 +114,15 @@ async fn msc4168_update_parent_spaces(
) )
.boxed() .boxed()
.await .await
.debug_inspect_err(|e| {
error!(
?parent_id,
old_room_id=?old_room_id,
new_room_id=?new_room_id,
%e,
"failed to send m.space.child to parent during room upgrade"
)
})
.ok(); .ok();
drop(state_lock); drop(state_lock);
} }
@@ -128,11 +146,14 @@ async fn msc4168_update_space_children(
// children. // children.
// In rooms that reference the old room via m.space.parent events... // In rooms that reference the old room via m.space.parent events...
// NOTE: Doing that would be expensive. We'll instead fetch rooms which the
// space claims are children.
let parents = services let parents = services
.rooms .rooms
.state_accessor .state_accessor
.room_state_keys(old_room_id, &StateEventType::SpaceParent) .room_state_keys(old_room_id, &StateEventType::SpaceChild)
.await?; .await
.debug_inspect(|k| debug!(?old_room_id, "Children: {k:?}"))?;
for raw_child_id in parents { for raw_child_id in parents {
let child_id = RoomId::parse(&raw_child_id)?; let child_id = RoomId::parse(&raw_child_id)?;
@@ -148,6 +169,15 @@ async fn msc4168_update_space_children(
old_room_id.as_str(), old_room_id.as_str(),
) )
.await .await
.debug_inspect_err(|e| {
error!(
?child_id,
old_room_id=?old_room_id,
new_room_id=?new_room_id,
%e,
"failed to fetch m.space.parent from child"
)
})
else { else {
// If the child does not have a parent event for this room, we can skip it. // If the child does not have a parent event for this room, we can skip it.
continue; continue;
@@ -169,6 +199,13 @@ async fn msc4168_update_space_children(
) )
.boxed() .boxed()
.await .await
.debug_inspect_err(|e| error!(
child_id=?child_id,
old_room_id=?old_room_id,
new_room_id=?new_room_id,
%e,
"failed to send updated m.space.parent to child during room upgrade"
))
.ok(); .ok();
// If the previous m.space.parent event has canonical set to true in content, // If the previous m.space.parent event has canonical set to true in content,
@@ -179,19 +216,25 @@ async fn msc4168_update_space_children(
.rooms .rooms
.timeline .timeline
.build_and_append_pdu( .build_and_append_pdu(
PartialPdu { PartialPdu::state(
event_type: StateEventType::SpaceParent.into(), old_room_id.as_str(),
content: to_raw_value(&assign!(parent.clone(), {canonical: false})) &assign!(parent.clone(), {canonical: false}),
.expect("event is valid, we just created it"), ),
state_key: Some(old_room_id.as_str().into()),
..Default::default()
},
sender, sender,
Some(&child_id), Some(&child_id),
&state_lock, &state_lock,
) )
.boxed() .boxed()
.await .await
.debug_inspect_err(|e| {
error!(
child_id=?child_id,
old_room_id=?old_room_id,
new_room_id=?new_room_id,
%e,
"failed to send non-canonical m.space.parent to child room"
)
})
.ok(); .ok();
} }
drop(state_lock); drop(state_lock);
@@ -334,6 +377,7 @@ pub(crate) async fn upgrade_room_route(
// risk of concurrent in-flight collisions. // risk of concurrent in-flight collisions.
services.rooms.state.mutex.lock("!new-room").await services.rooms.state.mutex.lock("!new-room").await
}; };
debug!("Upgrading {} to room version {}", &body.room_id, &body.new_version);
let create_event_id = services let create_event_id = services
.rooms .rooms
.timeline .timeline
@@ -372,6 +416,7 @@ pub(crate) async fn upgrade_room_route(
(Some(new_room_id), lock) (Some(new_room_id), lock)
}; };
debug!("Upgraded {} to {}", &body.room_id, replacement_room_id.as_deref().unwrap());
// Join the new room // Join the new room
services services
.rooms .rooms
@@ -446,6 +491,7 @@ pub(crate) async fn upgrade_room_route(
.expect("event is valid, we just deserialized and modified it"); .expect("event is valid, we just deserialized and modified it");
} }
debug!(%event_type, ?state_key, "Transferring state event to new room");
services services
.rooms .rooms
.timeline .timeline
@@ -473,6 +519,7 @@ pub(crate) async fn upgrade_room_route(
.boxed(); .boxed();
while let Some(alias) = local_aliases.next().await { while let Some(alias) = local_aliases.next().await {
debug!(?alias, "Migrating alias");
services services
.rooms .rooms
.alias .alias
@@ -488,6 +535,7 @@ pub(crate) async fn upgrade_room_route(
// 5. Send a `m.room.tombstone` event to the old room to indicate that it is not // 5. Send a `m.room.tombstone` event to the old room to indicate that it is not
// intended to be used any further. // intended to be used any further.
debug!(target=?body.room_id, "Sending tombstone to old room");
services services
.rooms .rooms
.timeline .timeline
@@ -529,6 +577,7 @@ pub(crate) async fn upgrade_room_route(
// 6. Modify the power levels in the old room to prevent sending of events and // 6. Modify the power levels in the old room to prevent sending of events and
// inviting new users // inviting new users
// Spec dictates that this is allowed to fail. // Spec dictates that this is allowed to fail.
debug!(target=?body.room_id, ?new_level, "Raising power level in old room to lock it");
services services
.rooms .rooms
.timeline .timeline
@@ -546,6 +595,7 @@ pub(crate) async fn upgrade_room_route(
.ok(); .ok();
// MSC4168: Update spaces that reference this room to point at the new room. // MSC4168: Update spaces that reference this room to point at the new room.
debug!("Updating parent spaces");
msc4168_update_parent_spaces( msc4168_update_parent_spaces(
&services, &services,
sender_user, sender_user,
@@ -553,8 +603,18 @@ pub(crate) async fn upgrade_room_route(
replacement_room_id.as_deref().unwrap(), replacement_room_id.as_deref().unwrap(),
) )
.await .await
.inspect_err(|e| {
error!(
old_room_id=?body.room_id,
new_room_id=?replacement_room_id.as_deref().unwrap(),
%e,
"failed to update parent spaces during room upgrade"
)
})
.ok(); .ok();
// MSC4168: Update child rooms to point at the new space, where possible // MSC4168: Update child rooms to point at the new space, where possible
debug!("Updating space children");
msc4168_update_space_children( msc4168_update_space_children(
&services, &services,
sender_user, sender_user,
@@ -562,6 +622,14 @@ pub(crate) async fn upgrade_room_route(
replacement_room_id.as_deref().unwrap(), replacement_room_id.as_deref().unwrap(),
) )
.await .await
.inspect_err(|e| {
error!(
old_room_id=?body.room_id,
new_room_id=?replacement_room_id.as_deref().unwrap(),
%e,
"failed to update space children during room upgrade"
)
})
.ok(); .ok();
// Return the replacement room id // Return the replacement room id