1use std::{collections::HashSet, iter::once, num::NonZeroUsize};
8
9use futures::{
10 FutureExt, StreamExt, TryFutureExt,
11 future::{join, try_join, try_join4},
12};
13use rand::seq::SliceRandom;
14use ruma::{
15 CanonicalJsonObject, EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, RoomId, ServerName,
16 api::Direction, events::TimelineEventType,
17};
18use serde::Deserialize;
19use serde_json::value::RawValue as RawJsonValue;
20use tuwunel_core::{
21 Err, Result, at, debug, debug_warn, implement, is_false,
22 matrix::{
23 PduEvent,
24 event::Event,
25 pdu::{PduCount, PduId, RawPduId},
26 },
27 utils::{
28 BoolExt, IterStream, ReadyExt,
29 future::{BoolExt as FutureBoolExt, TryExtExt},
30 },
31 validated, warn,
32};
33use tuwunel_database::Json;
34
35use super::{ExtractBody, bias_count};
36use crate::{
37 federation::Candidates,
38 fetcher::{Op, Opts},
39 rooms::state_accessor::plain_text_topic,
40};
41
42const BACKFILL_LIMIT: NonZeroUsize = NonZeroUsize::new(100).unwrap();
44
45const BACKFILL_ATTEMPT_LIMIT: NonZeroUsize = NonZeroUsize::new(5).unwrap();
46
47const BACKFILL_BATCH_ATTEMPTS: usize = 3;
48
49#[derive(Deserialize)]
52struct TimestampHit {
53 event_id: OwnedEventId,
54 origin_server_ts: MilliSecondsSinceUnixEpoch,
55}
56
57#[implement(super::Service)]
63#[tracing::instrument(name = "backfill", level = "debug", skip(self))]
64pub async fn backfill_if_required(&self, room_id: &RoomId, from: PduCount) -> Result {
65 let (first_pdu_count, first_pdu) = self.first_item_in_room(room_id).await?;
66
67 if first_pdu_count < from {
68 return Ok(());
69 }
70
71 if *first_pdu.event_type() == TimelineEventType::RoomCreate {
73 return Ok(());
74 }
75
76 let empty_room = self
77 .services
78 .state_cache
79 .room_joined_count(room_id)
80 .map_ok_or(true, |count| count <= 1);
81
82 let not_world_readable = self
83 .services
84 .state_accessor
85 .is_world_readable(room_id)
86 .map(is_false!());
87
88 if empty_room.and(not_world_readable).await {
90 return Ok(());
91 }
92
93 let mut eligible = self.backfill_candidates(room_id).await;
94
95 let no_backfill = || {
96 warn!(%room_id, "No servers could backfill, but backfill was needed");
97 Ok(())
98 };
99
100 if eligible.is_empty() {
104 return no_backfill();
105 }
106
107 for _ in 0..BACKFILL_BATCH_ATTEMPTS {
108 let opts = Opts::new(Op::Backfill, room_id.to_owned())
109 .event_id(first_pdu.event_id().to_owned())
110 .candidates(eligible.iter().cloned())
111 .attempt_limit(BACKFILL_ATTEMPT_LIMIT)
112 .backfill_limit(BACKFILL_LIMIT);
113
114 let Ok(outcome) = self.services.fetcher.fetch(opts).await else {
115 return no_backfill();
116 };
117
118 let pdus: Vec<Box<RawJsonValue>> = serde_json::from_slice(&outcome.bytes)?;
119 let batch_size = pdus.len();
120 let prepended = pdus
121 .into_iter()
122 .stream()
123 .fold(0_usize, async |prepended, pdu| {
124 let inserted = self
125 .backfill_pdu(room_id, &outcome.origin, pdu)
126 .await
127 .inspect_err(|e| debug_warn!(%room_id, %e, "Failed to add backfilled pdu"))
128 .unwrap_or(false);
129
130 prepended.saturating_add(usize::from(inserted))
131 })
132 .await;
133
134 debug!(
135 %room_id,
136 origin = %outcome.origin,
137 batch_size,
138 prepended,
139 "Processed backfill response",
140 );
141
142 if prepended > 0 {
143 return Ok(());
144 }
145
146 eligible.retain(|server| server != &outcome.origin);
147 if eligible.is_empty() {
148 break;
149 }
150 }
151
152 warn!(%room_id, "Backfill was required but prepended no events");
153 Ok(())
154}
155
156#[implement(super::Service)]
157async fn backfill_candidates(&self, room_id: &RoomId) -> Candidates {
158 let canonical_alias = self
159 .services
160 .state_accessor
161 .get_canonical_alias(room_id);
162
163 let power_levels = self
164 .services
165 .state_accessor
166 .get_power_levels(room_id);
167
168 let (canonical_alias, power_levels) = join(canonical_alias, power_levels).await;
169
170 let power_servers = power_levels
171 .iter()
172 .flat_map(|power| {
173 power
174 .rules
175 .privileged_creators
176 .iter()
177 .flat_map(|creators| creators.iter())
178 })
179 .chain(power_levels.iter().flat_map(|power| {
180 power
181 .users
182 .iter()
183 .filter_map(|(user_id, level)| level.gt(&power.users_default).then_some(user_id))
184 }))
185 .filter_map(|user_id| {
186 self.services
187 .globals
188 .user_is_local(user_id)
189 .is_false()
190 .then_some(user_id.server_name())
191 })
192 .collect::<HashSet<_>>();
193
194 let power_servers = {
195 let mut vec: Vec<_> = power_servers
196 .into_iter()
197 .map(ToOwned::to_owned)
198 .collect();
199
200 vec.shuffle(&mut rand::rng());
201 vec.into_iter().stream()
202 };
203
204 let canonical_room_alias_server = once(canonical_alias)
205 .filter_map(Result::ok)
206 .map(|alias| alias.server_name().to_owned())
207 .stream();
208
209 let trusted_servers = self
210 .services
211 .server
212 .config
213 .trusted_servers
214 .iter()
215 .map(ToOwned::to_owned)
216 .stream();
217
218 power_servers
219 .chain(canonical_room_alias_server)
220 .chain(trusted_servers)
221 .ready_filter(|server_name| !self.services.globals.server_is_ours(server_name))
222 .filter_map(async |server_name| {
223 self.services
224 .state_cache
225 .server_in_room(&server_name, room_id)
226 .await
227 .then_some(server_name)
228 })
229 .collect()
230 .await
231}
232
233#[implement(super::Service)]
234pub async fn get_event_id_near_ts_with_fallback(
240 &self,
241 room_id: &RoomId,
242 ts: MilliSecondsSinceUnixEpoch,
243 dir: Direction,
244) -> Result<(MilliSecondsSinceUnixEpoch, OwnedEventId)> {
245 let local = self.get_event_id_near_ts(room_id, ts, dir).await;
246
247 let federate = match &local {
248 | Err(_) => true,
249 | Ok((_, event_id)) =>
250 dir == Direction::Forward && self.is_start_edge_hit(room_id, event_id).await,
251 };
252
253 if !federate {
254 return local;
255 }
256
257 let candidates = self.backfill_candidates(room_id).await;
258
259 if candidates.is_empty() {
260 return local;
261 }
262
263 let opts = Opts::new(Op::TimestampToEvent, room_id.to_owned())
264 .ts(ts)
265 .dir(dir)
266 .candidates(candidates)
267 .checks(false);
268
269 let Ok(outcome) = self.services.fetcher.fetch(opts).await else {
270 return local;
271 };
272
273 let Ok(TimestampHit { event_id, origin_server_ts }) = serde_json::from_slice(&outcome.bytes)
274 else {
275 return local;
276 };
277
278 if let Ok((local_ts, local_id)) = &local
279 && !nearer(dir, origin_server_ts, *local_ts)
280 {
281 return Ok((*local_ts, local_id.clone()));
282 }
283
284 let Ok(pdu) = self
286 .backfill_event(room_id, &event_id, &outcome.origin)
287 .inspect_err(|e| debug_warn!(%room_id, error = ?e, "timestamp fallback backfill failed"))
288 .await
289 else {
290 return local;
291 };
292
293 let actual_ts = pdu.origin_server_ts();
294 let matches_direction = match dir {
295 | Direction::Forward => actual_ts >= ts,
296 | Direction::Backward => actual_ts <= ts,
297 };
298
299 if actual_ts != origin_server_ts || !matches_direction {
300 debug_warn!(
301 %room_id,
302 %event_id,
303 ?dir,
304 ?ts,
305 ?origin_server_ts,
306 ?actual_ts,
307 "timestamp fallback claim was inconsistent with the ingested event"
308 );
309 return local;
310 }
311
312 Ok((actual_ts, event_id))
313}
314
315#[implement(super::Service)]
316async fn is_start_edge_hit(&self, room_id: &RoomId, event_id: &EventId) -> bool {
317 self.first_item_in_room(room_id)
318 .await
319 .is_ok_and(|(_, first)| {
320 *first.event_type() != TimelineEventType::RoomCreate && first.event_id() == event_id
321 })
322}
323
324fn nearer(dir: Direction, a: MilliSecondsSinceUnixEpoch, b: MilliSecondsSinceUnixEpoch) -> bool {
325 match dir {
326 | Direction::Forward => a < b,
327 | Direction::Backward => a > b,
328 }
329}
330
331#[implement(super::Service)]
332async fn backfill_event(
333 &self,
334 room_id: &RoomId,
335 event_id: &EventId,
336 origin: &ServerName,
337) -> Result<PduEvent> {
338 let opts = Opts::new(Op::Backfill, room_id.to_owned())
339 .event_id(event_id.to_owned())
340 .candidates([origin.to_owned()])
341 .backfill_limit(BACKFILL_LIMIT);
342
343 let outcome = self.services.fetcher.fetch(opts).await?;
344
345 let pdus: Vec<Box<RawJsonValue>> = serde_json::from_slice(&outcome.bytes)?;
346
347 let ingestion = pdus
348 .into_iter()
349 .stream()
350 .fold(Ok(()), async |prior, pdu| {
351 let current = self
352 .backfill_pdu(room_id, &outcome.origin, pdu)
353 .map_ok(|_| ())
354 .inspect_err(
355 |e| debug_warn!(%room_id, error = ?e, "Failed to add backfilled pdu"),
356 )
357 .await;
358
359 prior.and(current)
360 })
361 .await;
362
363 match self.get_pdu_count(event_id).await {
364 | Err(error) => ingestion.and(Err(error)),
365 | Ok(_) => {
366 let pdu = self.get_pdu(event_id).await?;
367
368 if pdu.room_id() != room_id {
369 return Err!(Request(NotFound(
370 "Timestamp fallback target belongs to another room."
371 )));
372 }
373
374 Ok(pdu)
375 },
376 }
377}
378
379#[implement(super::Service)]
386#[tracing::instrument(skip(self), level = "debug")]
387pub async fn fetch_remote_event(&self, room_id: &RoomId, event_id: &EventId) -> Result {
388 let opts = Opts::new(Op::Event, room_id.to_owned())
389 .event_id(event_id.to_owned())
390 .checks(false);
391
392 let outcome = self.services.fetcher.fetch(opts).await?;
393
394 let pdu: Box<RawJsonValue> = serde_json::from_slice(&outcome.bytes)?;
395
396 self.backfill_pdu(room_id, &outcome.origin, pdu)
397 .await?;
398
399 Ok(())
400}
401
402#[implement(super::Service)]
409#[tracing::instrument(skip(self, pdu), level = "debug")]
410pub async fn backfill_pdu(
411 &self,
412 room_id: &RoomId,
413 origin: &ServerName,
414 pdu: Box<RawJsonValue>,
415) -> Result<bool> {
416 let parsed = self
417 .services
418 .event_handler
419 .parse_incoming_pdu(&pdu);
420
421 let mutex_lock = self
423 .services
424 .event_handler
425 .mutex_federation
426 .lock(room_id)
427 .map(Ok);
428
429 let ((_, event_id, value), mutex_lock) = try_join(parsed, mutex_lock).await?;
430
431 let existed = self
432 .services
433 .event_handler
434 .handle_incoming_pdu(origin, room_id, &event_id, value, false)
435 .await?
436 .map(at!(1))
437 .is_some_and(is_false!());
438
439 if existed {
441 return Ok(false);
442 }
443
444 let pdu = self.get_pdu(&event_id);
445
446 let value = self.get_pdu_json(&event_id);
447
448 let shortroomid = self.services.short.get_shortroomid(room_id);
449
450 let insert_lock = self.mutex_insert.lock(room_id).map(Ok);
451
452 let (pdu, value, shortroomid, insert_lock) =
453 try_join4(pdu, value, shortroomid, insert_lock).await?;
454
455 let count = self.services.globals.next_count();
458 let count: i64 = (*count).try_into()?;
459 let pdu_id: RawPduId = PduId {
460 shortroomid,
461 count: PduCount::Backfilled(validated!(0 - count)),
462 }
463 .into();
464
465 self.prepend_backfill_pdu(
467 &pdu_id,
468 room_id,
469 &event_id,
470 u64::from(pdu.origin_server_ts),
471 &value,
472 );
473 drop(insert_lock);
474
475 match pdu.kind {
476 | TimelineEventType::RoomMessage => {
477 if let Ok(ExtractBody { body: Some(body) }) = pdu.get_content() {
478 self.services
479 .search
480 .index_pdu(shortroomid, &pdu_id, &body);
481 }
482 },
483 | TimelineEventType::RoomTopic =>
484 if let Some(topic) = pdu.get_content().ok().and_then(plain_text_topic) {
485 self.services
486 .search
487 .index_pdu(shortroomid, &pdu_id, &topic);
488 },
489 | _ => {},
490 }
491
492 drop(mutex_lock);
493
494 debug!("Prepended backfill pdu");
495 Ok(true)
496}
497
498#[implement(super::Service)]
499fn prepend_backfill_pdu(
500 &self,
501 pdu_id: &RawPduId,
502 room_id: &RoomId,
503 event_id: &EventId,
504 origin_server_ts: u64,
505 json: &CanonicalJsonObject,
506) {
507 let mut txn = self.db.db.txn();
508
509 txn.raw_put(&self.db.pduid_pdu, pdu_id, Json(json));
510 txn.insert_raw(&self.db.eventid_pduid, event_id, pdu_id);
511 txn.del_raw(&self.db.eventid_outlierpdu, event_id);
512
513 let count_key = bias_count(pdu_id.count());
514 let key = (room_id, origin_server_ts, count_key);
515 txn.put_raw(&self.db.roomid_tscount_pducount, key, pdu_id.count());
516
517 txn.execute();
518}