From fa77d4cb52700550e94bfdabebeba70ccc2abf65 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 29 May 2026 14:34:36 +0100 Subject: [PATCH] Store calculated room ID on top-level Rather than validating and then re-calcualting it every time, let's just store it on the top-level. --- rust/src/events/formats/v1.rs | 2 +- rust/src/events/formats/v2v3.rs | 4 ++- rust/src/events/formats/v4.rs | 26 ++++++++--------- rust/src/events/formats/vmsc4242.rs | 11 +++----- rust/src/events/mod.rs | 44 +++++++++++++++++++++-------- rust/src/json.rs | 8 ++++++ 6 files changed, 60 insertions(+), 35 deletions(-) diff --git a/rust/src/events/formats/v1.rs b/rust/src/events/formats/v1.rs index 7fbc50a97c..09caabc597 100644 --- a/rust/src/events/formats/v1.rs +++ b/rust/src/events/formats/v1.rs @@ -35,7 +35,7 @@ use crate::events::formats::EventCommonFields; pub struct EventFormatV1 { pub auth_events: Vec<(String, HashMap)>, pub prev_events: Vec<(String, HashMap)>, - pub room_id: Box, + pub room_id: Arc, pub event_id: Arc, } diff --git a/rust/src/events/formats/v2v3.rs b/rust/src/events/formats/v2v3.rs index bd3a67b926..a62c68a4e4 100644 --- a/rust/src/events/formats/v2v3.rs +++ b/rust/src/events/formats/v2v3.rs @@ -29,6 +29,8 @@ //! [`SimpleAuthPrevEvents`] is shared with [`v4`](super::v4) since the //! flat-list encoding carries forward unchanged. +use std::sync::Arc; + use anyhow::{bail, Error}; use serde::{Deserialize, Serialize}; @@ -37,7 +39,7 @@ use crate::events::formats::EventCommonFields; /// Version-specific fields for room versions 3-10. #[derive(Serialize, Deserialize)] pub struct EventFormatV2V3 { - pub room_id: Box, + pub room_id: Arc, pub auth_events: Vec, pub prev_events: Vec, } diff --git a/rust/src/events/formats/v4.rs b/rust/src/events/formats/v4.rs index 77a55e2504..22a1a677f2 100644 --- a/rust/src/events/formats/v4.rs +++ b/rust/src/events/formats/v4.rs @@ -27,7 +27,7 @@ //! [`EventFormatV4::room_id`] and [`EventFormatV4::auth_event_ids`] //! expose the derived values to callers. -use std::borrow::Cow; +use std::sync::Arc; use anyhow::{bail, ensure, Error}; use serde::{Deserialize, Serialize}; @@ -45,15 +45,13 @@ pub struct EventFormatV4 { with = "crate::json::allow_missing", skip_serializing_if = "AllowMissing::is_absent" )] - pub room_id: AllowMissing>, + pub room_id: AllowMissing>, pub auth_events: Vec, pub prev_events: Vec, } impl EventFormatV4 { pub fn validate(&self, common_fields: &EventCommonFields) -> Result<(), Error> { - validate_optional_room_id(self.room_id.as_deref_opt(), common_fields)?; - // Ensure that we don't have an event_id set. if common_fields.other_fields.contains_key("event_id") { bail!("v4 events must not have an explicit event_id"); @@ -66,8 +64,8 @@ impl EventFormatV4 { &self, event_id: &str, common_fields: &EventCommonFields, - ) -> Result, Error> { - get_room_id_for_optional_room_id(self.room_id.as_deref_opt(), event_id, common_fields) + ) -> Result, Error> { + get_room_id_for_optional_room_id(self.room_id.as_ref_opt(), event_id, common_fields) } pub fn auth_event_ids(&self, common_fields: &EventCommonFields) -> Result, Error> { @@ -104,10 +102,10 @@ impl EventFormatV4 { /// Validation helper for v4+ events that can have an optional room ID. /// /// Returns the validated room ID (which will be `None` for create events). -pub fn validate_optional_room_id<'a>( - room_id: Option<&'a str>, +pub fn validate_optional_room_id( + room_id: Option<&Arc>, common_fields: &'_ EventCommonFields, -) -> Result, Error> { +) -> Result>, Error> { let is_create_event = common_fields.type_state_key_tuple() == Some((M_ROOM_CREATE, "")); match (is_create_event, room_id) { @@ -123,7 +121,7 @@ pub fn validate_optional_room_id<'a>( room_id ); - Ok(Some(room_id)) + Ok(Some(Arc::clone(room_id))) } // For create events, room_id must be absent. @@ -134,13 +132,13 @@ pub fn validate_optional_room_id<'a>( /// Room ID derivation helper for v4+ events, which can have an optional room /// ID. -pub fn get_room_id_for_optional_room_id<'a>( - room_id: Option<&'a str>, +pub fn get_room_id_for_optional_room_id( + room_id: Option<&Arc>, event_id: &str, common_fields: &EventCommonFields, -) -> Result, Error> { +) -> Result, Error> { match validate_optional_room_id(room_id, common_fields)? { - Some(room_id) => Ok(room_id.into()), + Some(room_id) => Ok(room_id), None => { // This is the create event, where the room ID is derived from the // event ID by replacing the leading '$' with '!'. diff --git a/rust/src/events/formats/vmsc4242.rs b/rust/src/events/formats/vmsc4242.rs index 896d479358..45d9a25068 100644 --- a/rust/src/events/formats/vmsc4242.rs +++ b/rust/src/events/formats/vmsc4242.rs @@ -24,7 +24,7 @@ //! //! [MSC4242]: https://github.com/matrix-org/matrix-spec-proposals/pull/4242 -use std::borrow::Cow; +use std::sync::Arc; use anyhow::bail; use anyhow::Error; @@ -34,7 +34,6 @@ use serde::{Deserialize, Serialize}; use crate::events::constants::event_type::M_ROOM_CREATE; use crate::events::formats::v4::get_room_id_for_optional_room_id; -use crate::events::formats::v4::validate_optional_room_id; use crate::events::formats::EventCommonFields; use crate::events::Event; use crate::json::AllowMissing; @@ -49,13 +48,11 @@ pub struct EventFormatVMSC4242 { with = "crate::json::allow_missing", skip_serializing_if = "AllowMissing::is_absent" )] - pub room_id: AllowMissing>, + pub room_id: AllowMissing>, } impl EventFormatVMSC4242 { pub fn validate(&self, common_fields: &EventCommonFields) -> Result<(), Error> { - validate_optional_room_id(self.room_id.as_deref_opt(), common_fields)?; - // Ensure that we don't have any `auth_events` or `event_id` fields // set. if common_fields.other_fields.contains_key("auth_events") { @@ -72,8 +69,8 @@ impl EventFormatVMSC4242 { &self, event_id: &str, common_fields: &EventCommonFields, - ) -> Result, Error> { - get_room_id_for_optional_room_id(self.room_id.as_deref_opt(), event_id, common_fields) + ) -> Result, Error> { + get_room_id_for_optional_room_id(self.room_id.as_ref_opt(), event_id, common_fields) } pub fn auth_event_ids(&self, event: &Event) -> PyResult> { diff --git a/rust/src/events/mod.rs b/rust/src/events/mod.rs index 6b1e3617d2..0df577dfba 100644 --- a/rust/src/events/mod.rs +++ b/rust/src/events/mod.rs @@ -48,7 +48,7 @@ //! string describing why auth rejected the event. //! -use std::{borrow::Cow, sync::Arc}; +use std::sync::Arc; use pyo3::{ exceptions::{PyAttributeError, PyKeyError, PyValueError}, @@ -137,6 +137,12 @@ pub struct Event { /// construction time and cached here. event_id: Arc, + /// The calculated room ID. + /// + /// For some room versions this may be derived, e.g. for create events in + /// v4. + room_id: Arc, + /// Synapse-internal per-event state that lives outside the federated /// JSON (e.g. outlier flag, soft-failure, stream positions). #[pyo3(get)] @@ -205,10 +211,32 @@ impl Event { } }; + let room_id = match &*parsed_event.specific_fields { + EventFormatEnum::V1(format) => Arc::clone(&format.room_id), + EventFormatEnum::V2V3(format) => Arc::clone(&format.room_id), + EventFormatEnum::V4(format) => format + .room_id(&event_id, &parsed_event.common_fields) + .map_err(|err| { + PyValueError::new_err(format!( + "Failed to calculate room_id for event {}: {}", + event_id, err + )) + })?, + EventFormatEnum::VMSC4242(format) => format + .room_id(&event_id, &parsed_event.common_fields) + .map_err(|err| { + PyValueError::new_err(format!( + "Failed to calculate room_id for event {}: {}", + event_id, err + )) + })?, + }; + Ok(Self { parsed_event, event_id, + room_id, room_version, rejected_reason, internal_metadata, @@ -341,6 +369,7 @@ impl Event { room_version: self.room_version, rejected_reason: self.rejected_reason.clone(), event_id: self.event_id.clone(), + room_id: self.room_id.clone(), }; Ok(new_event) } @@ -432,17 +461,8 @@ impl Event { } #[getter] - fn room_id(&self) -> PyResult> { - match &*self.parsed_event.specific_fields { - EventFormatEnum::V1(format) => Ok(format.room_id.as_ref().into()), - EventFormatEnum::V2V3(format) => Ok(format.room_id.as_ref().into()), - EventFormatEnum::V4(format) => { - Ok(format.room_id(&self.event_id, &self.parsed_event.common_fields)?) - } - EventFormatEnum::VMSC4242(format) => { - Ok(format.room_id(&self.event_id, &self.parsed_event.common_fields)?) - } - } + fn room_id(&self) -> &str { + &self.room_id } #[getter] diff --git a/rust/src/json.rs b/rust/src/json.rs index 696be79a0b..3e833c6707 100644 --- a/rust/src/json.rs +++ b/rust/src/json.rs @@ -64,6 +64,14 @@ impl AllowMissing { AllowMissing::Absent => None, } } + + /// Converts to `Option<&T>`. + pub fn as_ref_opt(&self) -> Option<&T> { + match self { + AllowMissing::Some(inner) => Some(inner), + AllowMissing::Absent => None, + } + } } /// A module that provides the serialization and deserialization logic for