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.
This commit is contained in:
Erik Johnston
2026-05-29 14:34:36 +01:00
parent 6fc928707b
commit fa77d4cb52
6 changed files with 60 additions and 35 deletions
+1 -1
View File
@@ -35,7 +35,7 @@ use crate::events::formats::EventCommonFields;
pub struct EventFormatV1 {
pub auth_events: Vec<(String, HashMap<String, String>)>,
pub prev_events: Vec<(String, HashMap<String, String>)>,
pub room_id: Box<str>,
pub room_id: Arc<str>,
pub event_id: Arc<str>,
}
+3 -1
View File
@@ -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<str>,
pub room_id: Arc<str>,
pub auth_events: Vec<String>,
pub prev_events: Vec<String>,
}
+12 -14
View File
@@ -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<Box<str>>,
pub room_id: AllowMissing<Arc<str>>,
pub auth_events: Vec<String>,
pub prev_events: Vec<String>,
}
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<Cow<'_, str>, Error> {
get_room_id_for_optional_room_id(self.room_id.as_deref_opt(), event_id, common_fields)
) -> Result<Arc<str>, 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<Vec<String>, 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<str>>,
common_fields: &'_ EventCommonFields,
) -> Result<Option<&'a str>, Error> {
) -> Result<Option<Arc<str>>, 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<str>>,
event_id: &str,
common_fields: &EventCommonFields,
) -> Result<Cow<'a, str>, Error> {
) -> Result<Arc<str>, 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 '!'.
+4 -7
View File
@@ -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<Box<str>>,
pub room_id: AllowMissing<Arc<str>>,
}
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<Cow<'_, str>, Error> {
get_room_id_for_optional_room_id(self.room_id.as_deref_opt(), event_id, common_fields)
) -> Result<Arc<str>, 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<Vec<String>> {
+32 -12
View File
@@ -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<str>,
/// The calculated room ID.
///
/// For some room versions this may be derived, e.g. for create events in
/// v4.
room_id: Arc<str>,
/// 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<Cow<'_, str>> {
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]
+8
View File
@@ -64,6 +64,14 @@ impl<T> AllowMissing<T> {
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