1use std::{
2 borrow::Borrow,
3 collections::HashMap,
4 mem::take,
5 sync::{
6 Arc,
7 atomic::{AtomicBool, AtomicU64, Ordering},
8 },
9};
10
11use futures::{FutureExt, StreamExt, TryFutureExt, TryStreamExt, future::join};
12use ruma::{
13 EventId, OwnedEventId, OwnedRoomId, RoomId, RoomVersionId,
14 events::{StateEventType, TimelineEventType},
15 room_version_rules::RoomVersionRules,
16};
17use tracing::{Instrument, Span};
18use tuwunel_core::{
19 Result, debug, debug_warn, defer, err, implement,
20 matrix::{
21 Event, PduEvent,
22 pdu::PrevEvents,
23 room_version::{self, from_create_event},
24 },
25 trace,
26 utils::{
27 math::fetch_add_usize,
28 stream::{BroadbandExt, IterStream, ReadyExt, WidebandExt},
29 },
30};
31
32use crate::rooms::{
33 short::{ShortStateHash, ShortStateKey},
34 state::IdMapState,
35 state_compressor::CompressedState,
36 state_res::{AuthCheckOutcome, auth_check},
37};
38
39#[cfg(test)]
40mod tests;
41
42type StateIds = HashMap<ShortStateKey, OwnedEventId>;
44
45#[derive(Debug)]
47pub struct LocalBuildReport {
48 pub state_len: Option<usize>,
49 pub visited: usize,
50 pub forks: usize,
51 pub gate_drops: usize,
52 pub memo_hits: usize,
53 pub fallback: Option<String>,
54}
55
56#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
61pub struct StateLocalMetrics {
62 pub walk_attempts: u64,
64 pub walk_resolved: u64,
66 pub fallback_absent: u64,
68 pub fallback_ceiling: u64,
70 pub fallback_auth_missing: u64,
72 pub fallback_all_committed: u64,
74 pub fallback_entries: u64,
76 pub fallback_canary: u64,
78 pub fallback_create_mismatch: u64,
80 pub fallback_unevaluable: u64,
82 pub fallback_error: u64,
84 pub walk_failures: u64,
86 pub gate_denials: u64,
88}
89
90#[derive(Default)]
91pub(super) struct StateLocalCounters {
92 walk_attempts: AtomicU64,
93 walk_resolved: AtomicU64,
94 fallback_absent: AtomicU64,
95 fallback_ceiling: AtomicU64,
96 fallback_auth_missing: AtomicU64,
97 fallback_all_committed: AtomicU64,
98 fallback_entries: AtomicU64,
99 fallback_canary: AtomicU64,
100 fallback_create_mismatch: AtomicU64,
101 fallback_unevaluable: AtomicU64,
102 fallback_error: AtomicU64,
103 walk_failures: AtomicU64,
104 gate_denials: AtomicU64,
105}
106
107struct WalkAttempt {
108 counters: Arc<StateLocalCounters>,
109 settled: bool,
110}
111
112#[implement(StateLocalCounters)]
113fn start_walk(&self) { self.walk_attempts.fetch_add(1, Ordering::Relaxed); }
114
115#[implement(StateLocalCounters)]
116fn settle_walk(&self, outcome: WalkOutcome) {
117 match outcome {
118 | WalkOutcome::Resolved => {
119 self.walk_resolved.fetch_add(1, Ordering::Relaxed);
120 },
121 | WalkOutcome::Fallback(fallback) => self.record_fallback(fallback),
122 | WalkOutcome::Failure => {
123 self.walk_failures.fetch_add(1, Ordering::Relaxed);
124 },
125 }
126}
127
128#[implement(StateLocalCounters)]
129fn record_fallback(&self, fallback: Fallback) {
130 let counter = match fallback {
131 | Fallback::Absent => &self.fallback_absent,
132 | Fallback::Ceiling => &self.fallback_ceiling,
133 | Fallback::AuthMissing => &self.fallback_auth_missing,
134 | Fallback::AllCommitted => &self.fallback_all_committed,
135 | Fallback::Entries => &self.fallback_entries,
136 | Fallback::Canary => &self.fallback_canary,
137 | Fallback::CreateMismatch => &self.fallback_create_mismatch,
138 | Fallback::Unevaluable => &self.fallback_unevaluable,
139 | Fallback::Error => &self.fallback_error,
140 };
141
142 counter.fetch_add(1, Ordering::Relaxed);
143}
144
145#[implement(StateLocalCounters)]
146fn add_gate_denials(&self, gate_denials: usize) {
147 if gate_denials == 0 {
148 return;
149 }
150
151 fetch_add_usize(&self.gate_denials, gate_denials, Ordering::Relaxed);
152}
153
154#[implement(StateLocalCounters)]
155fn snapshot(&self) -> StateLocalMetrics {
156 StateLocalMetrics {
157 walk_attempts: self.walk_attempts.load(Ordering::Relaxed),
158 walk_resolved: self.walk_resolved.load(Ordering::Relaxed),
159 fallback_absent: self.fallback_absent.load(Ordering::Relaxed),
160 fallback_ceiling: self.fallback_ceiling.load(Ordering::Relaxed),
161 fallback_auth_missing: self.fallback_auth_missing.load(Ordering::Relaxed),
162 fallback_all_committed: self
163 .fallback_all_committed
164 .load(Ordering::Relaxed),
165 fallback_entries: self.fallback_entries.load(Ordering::Relaxed),
166 fallback_canary: self.fallback_canary.load(Ordering::Relaxed),
167 fallback_create_mismatch: self
168 .fallback_create_mismatch
169 .load(Ordering::Relaxed),
170 fallback_unevaluable: self.fallback_unevaluable.load(Ordering::Relaxed),
171 fallback_error: self.fallback_error.load(Ordering::Relaxed),
172 walk_failures: self.walk_failures.load(Ordering::Relaxed),
173 gate_denials: self.gate_denials.load(Ordering::Relaxed),
174 }
175}
176
177#[implement(WalkAttempt)]
178fn start(counters: Arc<StateLocalCounters>) -> Self {
179 counters.start_walk();
180
181 Self { counters, settled: false }
182}
183
184#[implement(WalkAttempt)]
185fn settle(mut self, outcome: WalkOutcome, gate_denials: usize) {
186 self.counters.add_gate_denials(gate_denials);
187 self.counters.settle_walk(outcome);
188 self.settled = true;
189}
190
191impl Drop for WalkAttempt {
192 fn drop(&mut self) {
193 if !self.settled {
194 self.counters.settle_walk(WalkOutcome::Failure);
195 }
196 }
197}
198
199#[derive(Clone, Copy, Eq, PartialEq)]
202pub(super) enum WalkMode {
203 Active,
204 Shadow,
205}
206
207struct Walk<'a> {
209 room_id: &'a RoomId,
210 room_version: &'a RoomVersionId,
211 room_rules: RoomVersionRules,
212 create_event_id: &'a EventId,
213 mode: WalkMode,
214 max_nodes: usize,
215 top_prevs: PrevEvents,
216 class: HashMap<OwnedEventId, Class>,
217 nodes: Vec<Node>,
218 order: Vec<usize>,
219 frontier: HashMap<OwnedEventId, usize>,
220 resolved: HashMap<OwnedEventId, Arc<StateIds>>,
221 live_entries: usize,
222 peak_entries: usize,
223 forks: usize,
224 gate_drops: usize,
225 memo_hits: usize,
226 fallback: Option<Fallback>,
227 attempt: Option<WalkAttempt>,
228}
229
230struct Node {
232 pdu: PduEvent,
233 consumers: usize,
234}
235
236#[derive(Clone, Copy)]
238enum Class {
239 Committed(ShortStateHash),
241
242 Memoized,
244
245 Held(usize),
247}
248
249#[derive(Clone, Copy)]
251enum Fallback {
252 Absent,
253 Ceiling,
254 AuthMissing,
255 AllCommitted,
256 Entries,
257 Canary,
258 CreateMismatch,
259 Unevaluable,
260 Error,
261}
262
263#[derive(Clone, Copy)]
264enum WalkOutcome {
265 Resolved,
266 Fallback(Fallback),
267 Failure,
268}
269
270const MAX_LIVE_ENTRIES: usize = 1 << 19;
275
276#[implement(super::Service)]
283pub(super) async fn state_at_incoming_local<Pdu>(
284 &self,
285 room_id: &RoomId,
286 incoming_pdu: &Pdu,
287 room_version: &RoomVersionId,
288 create_event_id: &EventId,
289 mode: WalkMode,
290) -> Result<Option<StateIds>>
291where
292 Pdu: Event,
293{
294 let top_prevs = incoming_pdu
295 .prev_events()
296 .map(ToOwned::to_owned)
297 .collect();
298
299 let services = self.services.clone();
300 let room_id = room_id.to_owned();
301 let room_version = room_version.clone();
302 let create_event_id = create_event_id.to_owned();
303 let parent = Span::current();
304 let attempt = WalkAttempt::start(self.state_local.clone());
305
306 let task = self.services.server.runtime().spawn(
307 async move {
308 services
309 .event_handler
310 .walk_task(room_id, room_version, create_event_id, mode, top_prevs, attempt)
311 .await
312 }
313 .instrument(parent),
314 );
315
316 let abort = task.abort_handle();
318
319 defer! {{ abort.abort(); }};
320
321 task.await.unwrap_or_else(|error| {
322 debug_warn!(
323 %error,
324 "Local state build task failed; falling back to federation fetch.",
325 );
326
327 Ok(None)
328 })
329}
330
331#[implement(super::Service)]
335#[tracing::instrument(name = "local", level = "debug", skip_all)]
336async fn walk_task(
337 &self,
338 room_id: OwnedRoomId,
339 room_version: RoomVersionId,
340 create_event_id: OwnedEventId,
341 mode: WalkMode,
342 top_prevs: PrevEvents,
343 attempt: WalkAttempt,
344) -> Result<Option<StateIds>> {
345 let max_nodes = self
346 .services
347 .server
348 .config
349 .resolve_state_locally_max;
350
351 let mut walk = Walk::new(
352 &room_id,
353 &room_version,
354 &create_event_id,
355 mode,
356 max_nodes,
357 top_prevs,
358 Some(attempt),
359 )?;
360
361 let state = self.walk_state(&mut walk).await;
362 let state = state.inspect_err(|_| {
363 walk.settle(WalkOutcome::Failure);
364 })?;
365
366 debug!(
367 visited = walk.nodes.len(),
368 forks = walk.forks,
369 gate_drops = walk.gate_drops,
370 memo_hits = walk.memo_hits,
371 live_entries_peak = walk.peak_entries,
372 outcome = walk.fallback.map_or("resolved", Fallback::name),
373 "Local state build finished.",
374 );
375
376 if let Some(fallback) = walk.fallback {
377 debug_warn!(
378 reason = fallback.name(),
379 "Local state build falling back to federation fetch.",
380 );
381 }
382
383 let (state, outcome) = match (state, walk.fallback) {
384 | (Some(state), None) => (Some(state), WalkOutcome::Resolved),
385 | (None, Some(fallback)) => (None, WalkOutcome::Fallback(fallback)),
386 | _ => {
387 debug_assert!(false, "local walk state and fallback disagree");
388 (None, WalkOutcome::Failure)
389 },
390 };
391
392 walk.settle(outcome);
393
394 Ok(state)
395}
396
397#[implement(super::Service)]
401#[inline]
402#[must_use]
403pub fn state_local_metrics(&self) -> StateLocalMetrics { self.state_local.snapshot() }
404
405#[implement(super::Service)]
410pub async fn local_state_report(&self, event_id: &EventId) -> Result<LocalBuildReport> {
411 let pdu = self.services.timeline.get_pdu(event_id).await?;
412
413 let create_event = self
414 .services
415 .state_accessor
416 .room_state_get(pdu.room_id(), &StateEventType::RoomCreate, "")
417 .await?;
418
419 let room_version = from_create_event(&create_event)?;
420 let max_nodes = self
421 .services
422 .server
423 .config
424 .resolve_state_locally_max;
425
426 let top_prevs = pdu.prev_events().map(ToOwned::to_owned).collect();
427
428 let mut walk = Walk::new(
429 pdu.room_id(),
430 &room_version,
431 create_event.event_id(),
432 WalkMode::Shadow,
433 max_nodes,
434 top_prevs,
435 None,
436 )?;
437
438 let state = self.walk_state(&mut walk).await?;
439
440 Ok(LocalBuildReport {
441 state_len: state.map(|state| state.len()),
442 visited: walk.nodes.len(),
443 forks: walk.forks,
444 gate_drops: walk.gate_drops,
445 memo_hits: walk.memo_hits,
446 fallback: walk
447 .fallback
448 .map(|fallback| fallback.name().to_owned()),
449 })
450}
451
452#[implement(super::Service)]
455async fn walk_state(&self, walk: &mut Walk<'_>) -> Result<Option<StateIds>> {
456 self.walk_discover(walk).await?;
457
458 if walk.fallback.is_some() {
459 return Ok(None);
460 }
461
462 self.walk_build(walk)
463 .boxed() .await
465}
466
467#[implement(super::Service)]
471async fn walk_discover(&self, walk: &mut Walk<'_>) -> Result {
472 let mut stack: Vec<(OwnedEventId, bool)> = walk
473 .top_prevs
474 .iter()
475 .map(|prev| (prev.clone(), false))
476 .collect();
477
478 while let Some((event_id, expanded)) = stack.pop() {
479 self.services.server.check_running()?;
480
481 if expanded {
482 let Some(Class::Held(index)) = walk.class.get(&event_id).copied() else {
484 debug_assert!(false, "expanded stack entries are held nodes");
485 walk.fallback = Some(Fallback::Error);
486 return Ok(());
487 };
488
489 walk.order.push(index);
490 continue;
491 }
492
493 if walk.class.contains_key(&event_id) {
494 continue;
495 }
496
497 if let Ok(shortstatehash) = self
498 .services
499 .state
500 .pdu_shortstatehash(&event_id)
501 .await
502 {
503 walk.class
504 .insert(event_id, Class::Committed(shortstatehash));
505
506 continue;
507 }
508
509 if self
510 .db
511 .eventid_resolvedstate
512 .exists(&event_id)
513 .await
514 .is_ok()
515 {
516 walk.class.insert(event_id, Class::Memoized);
517 continue;
518 }
519
520 let Ok(pdu) = self.services.timeline.get_pdu(&event_id).await else {
521 trace!(%event_id, "Ancestor is not held locally.");
522 walk.fallback = Some(Fallback::Absent);
523 return Ok(());
524 };
525
526 if walk.nodes.len() >= walk.max_nodes {
527 walk.fallback = Some(Fallback::Ceiling);
528 return Ok(());
529 }
530
531 if pdu.prev_events().next().is_none() {
532 debug_warn!(%event_id, "Held uncommitted ancestor has no prev events.");
533 walk.fallback = Some(Fallback::Error);
534 return Ok(());
535 }
536
537 if !self.walk_auth_present(walk, &pdu).await {
538 walk.fallback = Some(Fallback::AuthMissing);
539 return Ok(());
540 }
541
542 walk.class
543 .insert(event_id.clone(), Class::Held(walk.nodes.len()));
544
545 stack.push((event_id, true));
546 stack.extend(
547 pdu.prev_events()
548 .map(|prev| (prev.to_owned(), false)),
549 );
550 walk.nodes.push(Node { pdu, consumers: 0 });
551 }
552
553 if walk.nodes.is_empty() {
554 walk.fallback = Some(Fallback::AllCommitted);
557 return Ok(());
558 }
559
560 walk.count_consumers();
561
562 Ok(())
563}
564
565#[implement(super::Service)]
569async fn walk_auth_present(&self, walk: &Walk<'_>, pdu: &PduEvent) -> bool {
570 let is_hydra = !walk
571 .room_rules
572 .event_format
573 .allow_room_create_in_auth_events;
574
575 let not_create = *pdu.kind() != TimelineEventType::RoomCreate;
576 let hydra_create_id = (not_create && is_hydra)
577 .then(|| pdu.room_id().as_event_id().ok())
578 .flatten();
579
580 pdu.auth_events()
581 .chain(hydra_create_id.as_deref())
582 .stream()
583 .all(|auth_id| self.services.timeline.pdu_exists(auth_id))
584 .await
585}
586
587#[implement(super::Service)]
590async fn walk_build(&self, walk: &mut Walk<'_>) -> Result<Option<StateIds>> {
591 let order = take(&mut walk.order);
592 for index in order {
593 self.services.server.check_running()?;
594
595 if !self.walk_node(walk, index).await {
596 return Ok(None);
597 }
598 }
599
600 let top_prevs = take(&mut walk.top_prevs);
601 let state = match top_prevs.as_slice() {
602 | [prev] => self.state_after(walk, prev).await,
603 | _ => self.fork_resolve(walk, &top_prevs, None).await,
604 };
605
606 let Some(state) = state else {
607 return Ok(None);
608 };
609
610 let create_entry = self
613 .services
614 .short
615 .get_shortstatekey(&StateEventType::RoomCreate, "")
616 .await
617 .ok()
618 .and_then(|shortstatekey| state.get(&shortstatekey))
619 .map(AsRef::as_ref);
620
621 if state.is_empty() || create_entry != Some(walk.create_event_id) {
622 walk.fallback = Some(Fallback::CreateMismatch);
623 return Ok(None);
624 }
625
626 walk.resolved.clear();
627
628 let state = Arc::try_unwrap(state).unwrap_or_else(|state| (*state).clone());
629
630 Ok(Some(state))
631}
632
633#[implement(super::Service)]
636async fn walk_node(&self, walk: &mut Walk<'_>, index: usize) -> bool {
637 let node = &walk.nodes[index];
638 let event_id = node.pdu.event_id().to_owned();
639 let prevs: PrevEvents = node
640 .pdu
641 .prev_events()
642 .map(ToOwned::to_owned)
643 .collect();
644
645 let before = match prevs.as_slice() {
646 | [prev] => self.state_after(walk, prev).await,
647 | _ =>
648 self.fork_resolve(walk, &prevs, Some(&event_id))
649 .await,
650 };
651
652 let Some(before) = before else {
653 return false;
654 };
655
656 let after = match walk.nodes[index].pdu.state_key() {
657 | None => Ok(before),
658 | Some(_) =>
659 self.gated_fold(
660 &walk.room_rules,
661 &mut walk.gate_drops,
662 &walk.nodes[index].pdu,
663 &before,
664 )
665 .await,
666 };
667
668 let after = after.inspect_err(|error| {
669 debug_warn!(event_id = %event_id, %error, "Auth gate could not be evaluated.");
670 });
671
672 let Ok(after) = after else {
673 walk.fallback = Some(Fallback::Unevaluable);
674 return false;
675 };
676
677 if !walk.retain(event_id, after) {
678 return false;
679 }
680
681 walk.release(&prevs);
682
683 true
684}
685
686#[implement(super::Service)]
689async fn state_after(&self, walk: &mut Walk<'_>, event_id: &EventId) -> Option<Arc<StateIds>> {
690 if let Some(state) = walk.resolved.get(event_id) {
691 return Some(state.clone());
692 }
693
694 let state = match walk.class.get(event_id).copied() {
695 | Some(Class::Committed(shortstatehash)) =>
696 self.committed_state_after(walk, event_id, shortstatehash)
697 .await,
698 | Some(Class::Memoized) => self.memoized_state_after(walk, event_id).await,
699 | Some(Class::Held(_)) | None => {
700 debug_assert!(false, "held nodes resolve before their consumers");
701 walk.fallback = Some(Fallback::Error);
702 None
703 },
704 }?;
705
706 walk.retain(event_id.to_owned(), state.clone())
707 .then_some(state)
708}
709
710#[implement(super::Service)]
718async fn committed_state_after(
719 &self,
720 walk: &mut Walk<'_>,
721 event_id: &EventId,
722 shortstatehash: ShortStateHash,
723) -> Option<Arc<StateIds>> {
724 let pdu = self.services.timeline.get_pdu(event_id);
725
726 let state = self
727 .services
728 .state_accessor
729 .state_full_ids_strict(shortstatehash)
730 .try_collect::<StateIds>();
731
732 let (pdu, state) = join(pdu, state).await;
733 let Ok(mut state) = state.inspect_err(|e| {
734 debug_warn!(%event_id, %e, "Failed loading complete committed state.");
735 }) else {
736 walk.fallback = Some(Fallback::Unevaluable);
737 return None;
738 };
739
740 let Ok(pdu) = pdu.inspect_err(|e| {
741 debug_warn!(%event_id, %e, "Failed loading committed event.");
742 }) else {
743 walk.fallback = Some(Fallback::Error);
744 return None;
745 };
746
747 if let Some(state_key) = pdu.state_key() {
748 let event_type = pdu.event_type().to_cow_str().into();
749 let shortstatekey = self
750 .services
751 .short
752 .get_or_create_shortstatekey(&event_type, state_key)
753 .await;
754
755 state.insert(shortstatekey, event_id.to_owned());
756 }
757
758 Some(Arc::new(state))
759}
760
761#[implement(super::Service)]
765async fn memoized_state_after(
766 &self,
767 walk: &mut Walk<'_>,
768 event_id: &EventId,
769) -> Option<Arc<StateIds>> {
770 walk.memo_hits = walk.memo_hits.saturating_add(1);
771
772 let state = self.cached_resolved_state(event_id);
773
774 let pdu = self
775 .services
776 .timeline
777 .get_pdu(event_id)
778 .inspect_err(|e| debug_warn!(%event_id, %e, "Failed loading memoized event."));
779
780 let (state, pdu) = join(state, pdu).await;
781
782 let state = match state {
783 | Ok(Some(state)) => state,
784 | Ok(None) => {
785 walk.fallback = Some(Fallback::Canary);
786 return None;
787 },
788 | Err(e) => {
789 debug_warn!(%event_id, %e, "Failed loading complete memoized state.");
790 walk.fallback = Some(Fallback::Unevaluable);
791 return None;
792 },
793 };
794
795 let Ok(pdu) = pdu else {
796 walk.fallback = Some(Fallback::Error);
797 return None;
798 };
799
800 let before = Arc::new(state);
801 if pdu.state_key().is_none() {
802 return Some(before);
803 }
804
805 let after = self
806 .gated_fold(&walk.room_rules, &mut walk.gate_drops, &pdu, &before)
807 .await;
808
809 match after {
810 | Ok(after) => Some(after),
811 | Err(error) => {
812 debug_warn!(%event_id, %error, "Memoized auth gate could not be evaluated.");
813 walk.fallback = Some(Fallback::Unevaluable);
814 None
815 },
816 }
817}
818
819#[implement(super::Service)]
825async fn gated_fold(
826 &self,
827 room_rules: &RoomVersionRules,
828 gate_drops: &mut usize,
829 pdu: &PduEvent,
830 before: &Arc<StateIds>,
831) -> Result<Arc<StateIds>> {
832 let create_shortstatekey = self
833 .services
834 .short
835 .get_shortstatekey(&StateEventType::RoomCreate, "")
836 .await?;
837
838 if !before.contains_key(&create_shortstatekey) {
839 return Err(err!(Database("State before event is missing the room create event.")));
840 }
841
842 let state_fetch = IdMapState { services: &self.services, ids: before };
843
844 if let AuthCheckOutcome::Deny(error) =
845 auth_check(room_rules, pdu, &*self.services.timeline, state_fetch).await?
846 {
847 debug!(event_id = %pdu.event_id(), %error, "Auth gate rejected fold.");
848 *gate_drops = gate_drops.saturating_add(1);
849 return Ok(before.clone());
850 }
851
852 let state_key = pdu.state_key().expect("only state events fold");
853
854 let event_type = pdu.event_type().to_cow_str().into();
855 let shortstatekey = self
856 .services
857 .short
858 .get_or_create_shortstatekey(&event_type, state_key)
859 .await;
860
861 let mut state = StateIds::clone(before);
862 state.insert(shortstatekey, pdu.event_id().to_owned());
863
864 Ok(Arc::new(state))
865}
866
867#[implement(super::Service)]
871async fn fork_resolve(
872 &self,
873 walk: &mut Walk<'_>,
874 prevs: &[OwnedEventId],
875 memo_event_id: Option<&EventId>,
876) -> Option<Arc<StateIds>> {
877 walk.forks = walk.forks.saturating_add(1);
878
879 let mut afters = Vec::with_capacity(prevs.len());
881 for prev in prevs {
882 afters.push(self.state_after(walk, prev).await?);
883 }
884
885 let (room_id, room_version) = (walk.room_id, walk.room_version);
886 let fork_states: Result<Vec<_>> = afters
887 .iter()
888 .stream()
889 .wide_then(async |after| {
890 let state = after
891 .iter()
892 .map(|(shortstatekey, event_id)| (*shortstatekey, event_id));
893
894 self.fork_state(state).await
895 })
896 .try_collect()
897 .await;
898
899 let Ok(fork_states) = fork_states.inspect_err(|e| {
900 debug_warn!(%e, "Failed converting complete fork state.");
901 }) else {
902 walk.fallback = Some(Fallback::Unevaluable);
903 return None;
904 };
905
906 let chain_complete = AtomicBool::new(true);
907 let auth_chains = prevs
908 .iter()
909 .zip(&afters)
910 .stream()
911 .wide_then(|(prev_event, after)| {
912 self.fork_chain_strict(
913 room_id,
914 room_version,
915 after.values().map(Borrow::borrow),
916 &chain_complete,
917 )
918 .inspect_err(move |e| {
919 debug_warn!(%prev_event, %e, "Failed loading complete fork auth chain.");
920 })
921 })
922 .ready_filter_map(Result::ok);
923
924 let resolved = self
925 .state_resolution(
926 room_id,
927 room_version,
928 fork_states.into_iter().stream(),
929 auth_chains,
930 Some(&chain_complete),
931 )
932 .await;
933
934 if !chain_complete.load(Ordering::Relaxed) {
937 debug_warn!("Polled fork auth chain was incomplete.");
938 walk.fallback = Some(Fallback::Unevaluable);
939 return None;
940 }
941
942 let Ok(resolved) = resolved else {
943 walk.fallback = Some(Fallback::Error);
944 return None;
945 };
946
947 let state: StateIds = resolved
948 .into_iter()
949 .stream()
950 .broad_then(async |((event_type, state_key), event_id)| {
951 self.services
952 .short
953 .get_or_create_shortstatekey(&event_type, &state_key)
954 .map(move |shortstatekey| (shortstatekey, event_id))
955 .await
956 })
957 .collect()
958 .await;
959
960 if let Some(event_id) = memo_event_id.filter(|_| walk.mode == WalkMode::Active) {
961 let compressed: Arc<CompressedState> = self
964 .services
965 .state_compressor
966 .compress_state_events(
967 state
968 .iter()
969 .map(|(shortstatekey, event_id)| (shortstatekey, event_id.borrow())),
970 )
971 .collect()
972 .map(Arc::new)
973 .await;
974
975 self.cache_resolved_state(walk.room_id, event_id, compressed)
976 .await;
977 }
978
979 Some(Arc::new(state))
980}
981
982impl<'a> Walk<'a> {
983 fn new(
984 room_id: &'a RoomId,
985 room_version: &'a RoomVersionId,
986 create_event_id: &'a EventId,
987 mode: WalkMode,
988 max_nodes: usize,
989 top_prevs: PrevEvents,
990 attempt: Option<WalkAttempt>,
991 ) -> Result<Self> {
992 Ok(Self {
993 room_id,
994 room_version,
995 room_rules: room_version::rules(room_version)?,
996 create_event_id,
997 mode,
998 max_nodes,
999 top_prevs,
1000 class: HashMap::new(),
1001 nodes: Vec::new(),
1002 order: Vec::new(),
1003 frontier: HashMap::new(),
1004 resolved: HashMap::new(),
1005 live_entries: 0,
1006 peak_entries: 0,
1007 forks: 0,
1008 gate_drops: 0,
1009 memo_hits: 0,
1010 fallback: None,
1011 attempt,
1012 })
1013 }
1014
1015 fn settle(&mut self, outcome: WalkOutcome) {
1016 if let Some(attempt) = self.attempt.take() {
1017 attempt.settle(outcome, self.gate_drops);
1018 }
1019 }
1020
1021 fn count_consumers(&mut self) {
1024 let mut held = vec![0_usize; self.nodes.len()];
1025
1026 let edges = self
1027 .nodes
1028 .iter()
1029 .flat_map(|node| node.pdu.prev_events())
1030 .chain(self.top_prevs.iter().map(AsRef::as_ref));
1031
1032 for prev in edges {
1033 match self.class.get(prev).copied() {
1034 | Some(Class::Held(index)) => held[index] = held[index].saturating_add(1),
1035 | Some(_) => {
1036 let consumers = self.frontier.entry(prev.to_owned()).or_default();
1037
1038 *consumers = consumers.saturating_add(1);
1039 },
1040 | None => debug_assert!(false, "every walk edge is classified"),
1041 }
1042 }
1043
1044 for (node, consumers) in self.nodes.iter_mut().zip(held) {
1045 node.consumers = consumers;
1046 }
1047 }
1048
1049 fn retain(&mut self, event_id: OwnedEventId, state: Arc<StateIds>) -> bool {
1053 let live_entries = self.live_entries.saturating_add(state.len());
1054 if live_entries > MAX_LIVE_ENTRIES {
1055 self.fallback = Some(Fallback::Entries);
1056 return false;
1057 }
1058
1059 self.live_entries = live_entries;
1060 self.peak_entries = self.peak_entries.max(live_entries);
1061 self.resolved.insert(event_id, state);
1062
1063 true
1064 }
1065
1066 fn release(&mut self, prevs: &[OwnedEventId]) {
1069 for prev in prevs {
1070 let remaining = match self.class.get(prev).copied() {
1071 | Some(Class::Held(index)) => {
1072 let node = &mut self.nodes[index];
1073 node.consumers = node.consumers.saturating_sub(1);
1074 node.consumers
1075 },
1076 | _ => {
1077 let Some(consumers) = self.frontier.get_mut(prev) else {
1078 continue;
1079 };
1080
1081 *consumers = consumers.saturating_sub(1);
1082 *consumers
1083 },
1084 };
1085
1086 if remaining == 0
1087 && let Some(state) = self.resolved.remove(prev)
1088 {
1089 self.live_entries = self.live_entries.saturating_sub(state.len());
1090 }
1091 }
1092 }
1093}
1094
1095impl Drop for Walk<'_> {
1096 fn drop(&mut self) { self.settle(WalkOutcome::Failure); }
1097}
1098
1099impl Fallback {
1100 fn name(self) -> &'static str {
1101 match self {
1102 | Self::Absent => "absent",
1103 | Self::Ceiling => "ceiling",
1104 | Self::AuthMissing => "auth_missing",
1105 | Self::AllCommitted => "all_committed",
1106 | Self::Entries => "entries",
1107 | Self::Canary => "canary",
1108 | Self::CreateMismatch => "create_mismatch",
1109 | Self::Unevaluable => "unevaluable",
1110 | Self::Error => "error",
1111 }
1112 }
1113}