Adjust federation send handler sans applying topological sort.

Signed-off-by: Jason Volk <jason@zemos.net>
This commit is contained in:
Jason Volk
2026-03-05 11:28:23 +00:00
parent 0ecdb86aca
commit 513c1184fe

View File

@@ -32,12 +32,13 @@ use tuwunel_core::{
debug_warn, defer, err, error, debug_warn, defer, err, error,
itertools::Itertools, itertools::Itertools,
result::LogErr, result::LogErr,
smallvec::SmallVec,
trace, trace,
utils::{ utils::{
IterStream, ReadyExt,
debug::str_truncated, debug::str_truncated,
future::TryExtExt,
millis_since_unix_epoch, millis_since_unix_epoch,
stream::{BroadbandExt, TryBroadbandExt, automatic_width}, stream::{BroadbandExt, IterStream, ReadyExt, TryBroadbandExt, automatic_width},
}, },
warn, warn,
}; };
@@ -49,6 +50,9 @@ use tuwunel_service::{
use crate::Ruma; use crate::Ruma;
type ResolvedMap = BTreeMap<OwnedEventId, Result>; type ResolvedMap = BTreeMap<OwnedEventId, Result>;
type RoomsPdus = SmallVec<[RoomPdus; 1]>;
type RoomPdus = (OwnedRoomId, TxnPdus);
type TxnPdus = SmallVec<[(usize, Pdu); 1]>;
type Pdu = (OwnedRoomId, OwnedEventId, CanonicalJsonObject); type Pdu = (OwnedRoomId, OwnedEventId, CanonicalJsonObject);
/// # `PUT /_matrix/federation/v1/send/{txnId}` /// # `PUT /_matrix/federation/v1/send/{txnId}`
@@ -99,22 +103,27 @@ pub(crate) async fn send_transaction_message_route(
.pdus .pdus
.iter() .iter()
.stream() .stream()
.broad_then(|pdu| services.event_handler.parse_incoming_pdu(pdu)) .enumerate()
.inspect_err(|e| debug_warn!("Could not parse PDU: {e}")) .broad_filter_map(|(i, pdu)| {
.ready_filter_map(Result::ok); services
.event_handler
.parse_incoming_pdu(pdu)
.inspect_err(move |e| debug_warn!("Could not parse PDU[{i}]: {e}"))
.map_ok(move |pdu| (i, pdu))
.ok()
});
let edus = body let edus = body
.edus .edus
.iter() .iter()
.map(|edu| edu.json().get()) .stream()
.map(serde_json::from_str) .enumerate()
.filter_map(Result::ok) .ready_filter_map(|(i, edu)| {
.stream(); serde_json::from_str(edu.json().get())
.inspect_err(|e| debug_warn!("Could not parse EDU[{i}]: {e}"))
trace!( .map(|edu| (i, edu))
elapsed = ?txn_start_time.elapsed(), .ok()
"Parsed txn", });
);
let results = handle( let results = handle(
&services, &services,
@@ -156,8 +165,8 @@ async fn handle(
origin: &ServerName, origin: &ServerName,
txn_id: &TransactionId, txn_id: &TransactionId,
started: Instant, started: Instant,
pdus: impl Stream<Item = Pdu> + Send, pdus: impl Stream<Item = (usize, Pdu)> + Send,
edus: impl Stream<Item = Edu> + Send, edus: impl Stream<Item = (usize, Edu)> + Send,
) -> Result<ResolvedMap> { ) -> Result<ResolvedMap> {
let results = handle_pdus(services, client, origin, txn_id, started, pdus).await?; let results = handle_pdus(services, client, origin, txn_id, started, pdus).await?;
@@ -172,25 +181,24 @@ async fn handle_pdus(
origin: &ServerName, origin: &ServerName,
txn_id: &TransactionId, txn_id: &TransactionId,
started: Instant, started: Instant,
pdus: impl Stream<Item = Pdu> + Send, pdus: impl Stream<Item = (usize, Pdu)> + Send,
) -> Result<ResolvedMap> { ) -> Result<ResolvedMap> {
// group pdus by room pdus.collect()
let pdus = pdus .map(Ok)
.enumerate() .map_ok(|pdus: TxnPdus| {
.collect()
.map(|mut pdus: Vec<_>| {
pdus.sort_by(|(_, (room_a, ..)), (_, (room_b, ..))| room_a.cmp(room_b));
pdus.into_iter() pdus.into_iter()
.sorted_by(|(_, (room_a, ..)), (_, (room_b, ..))| room_a.cmp(room_b))
.into_grouping_map_by(|(_, (room_id, ..))| room_id.clone()) .into_grouping_map_by(|(_, (room_id, ..))| room_id.clone())
.collect() .collect()
.into_iter()
.try_stream()
}) })
.await; .try_flatten_stream()
.try_collect::<RoomsPdus>()
// we can evaluate rooms concurrently .map_ok(IntoIterator::into_iter)
let results: ResolvedMap = pdus .map_ok(IterStream::try_stream)
.into_iter() .try_flatten_stream()
.try_stream() .broad_and_then(async |(room_id, pdus)| {
.broad_and_then(async |(room_id, pdus): (_, Vec<_>)| {
handle_room(services, client, origin, txn_id, started, room_id, pdus.into_iter()) handle_room(services, client, origin, txn_id, started, room_id, pdus.into_iter())
.map_ok(ResolvedMap::into_iter) .map_ok(ResolvedMap::into_iter)
.map_ok(IterStream::try_stream) .map_ok(IterStream::try_stream)
@@ -198,9 +206,7 @@ async fn handle_pdus(
}) })
.try_flatten() .try_flatten()
.try_collect() .try_collect()
.await?; .await
Ok(results)
} }
#[tracing::instrument( #[tracing::instrument(
@@ -218,24 +224,24 @@ async fn handle_room(
ref room_id: OwnedRoomId, ref room_id: OwnedRoomId,
pdus: impl Iterator<Item = (usize, Pdu)> + Send, pdus: impl Iterator<Item = (usize, Pdu)> + Send,
) -> Result<ResolvedMap> { ) -> Result<ResolvedMap> {
let _room_lock = services services
.event_handler .event_handler
.mutex_federation .mutex_federation
.lock(room_id) .lock(room_id)
.await; .then(async |_lock| {
pdus.enumerate()
pdus.enumerate() .try_stream()
.try_stream() .and_then(async |pdu| {
.and_then(|(ri, (ti, (room_id, event_id, value)))| { services.server.check_running().map(|()| pdu) // interruption point
handle_pdu( })
services, .and_then(|(ri, (ti, (room_id, event_id, value)))| {
(origin, txn_id, txn_start_time, ti), let meta = (origin, txn_id, txn_start_time, ti);
(room_id, event_id, ri), let pdu = (ri, (room_id, event_id, value));
value, handle_pdu(services, meta, pdu).map(Ok)
) })
.try_collect()
.await
}) })
.try_collect()
.boxed()
.await .await
} }
@@ -248,11 +254,8 @@ async fn handle_room(
async fn handle_pdu( async fn handle_pdu(
services: &Services, services: &Services,
(origin, txn_id, txn_start_time, ti): (&ServerName, &TransactionId, Instant, usize), (origin, txn_id, txn_start_time, ti): (&ServerName, &TransactionId, Instant, usize),
(ref room_id, event_id, ri): (OwnedRoomId, OwnedEventId, usize), (ri, (ref room_id, event_id, value)): (usize, Pdu),
value: CanonicalJsonObject, ) -> (OwnedEventId, Result) {
) -> Result<(OwnedEventId, Result)> {
services.server.check_running()?;
let pdu_start_time = Instant::now(); let pdu_start_time = Instant::now();
let completed: AtomicBool = Default::default(); let completed: AtomicBool = Default::default();
defer! {{ defer! {{
@@ -279,6 +282,7 @@ async fn handle_pdu(
.event_handler .event_handler
.handle_incoming_pdu(origin, room_id, &event_id, value, true) .handle_incoming_pdu(origin, room_id, &event_id, value, true)
.map_ok(|_| ()) .map_ok(|_| ())
.boxed()
.await; .await;
completed.store(true, Ordering::Release); completed.store(true, Ordering::Release);
@@ -289,7 +293,7 @@ async fn handle_pdu(
"Finished PDU", "Finished PDU",
); );
Ok((event_id.clone(), result)) (event_id.clone(), result)
} }
#[tracing::instrument(name = "edus", level = "debug", skip_all)] #[tracing::instrument(name = "edus", level = "debug", skip_all)]
@@ -298,13 +302,12 @@ async fn handle_edus(
client: &IpAddr, client: &IpAddr,
origin: &ServerName, origin: &ServerName,
txn_id: &TransactionId, txn_id: &TransactionId,
edus: impl Stream<Item = Edu> + Send, edus: impl Stream<Item = (usize, Edu)> + Send,
) -> Result { ) -> Result {
edus.enumerate() edus.for_each_concurrent(automatic_width(), |(i, edu)| {
.for_each_concurrent(automatic_width(), |(i, edu)| { handle_edu(services, client, origin, txn_id, i, edu)
handle_edu(services, client, origin, txn_id, i, edu) })
}) .await;
.await;
Ok(()) Ok(())
} }