Skip to main content

tuwunel_service/rooms/timeline/
pdus.rs

1use std::borrow::Borrow;
2
3use futures::{
4	Stream, TryFutureExt, TryStreamExt,
5	future::Either::{Left, Right},
6};
7use ruma::{MilliSecondsSinceUnixEpoch, RoomId, UInt, UserId, api::Direction};
8use tuwunel_core::{
9	Result, at, err, implement,
10	matrix::pdu::{PduCount, PduEvent},
11	trace,
12	utils::{
13		result::LogErr,
14		stream::{TryIgnore, TryReadyExt, TryWidebandExt},
15	},
16	warn,
17};
18use tuwunel_database::{KeyVal, keyval::Val};
19
20use super::{PduId, RawPduId};
21
22pub type PdusIterItem = (PduCount, PduEvent);
23
24/// Offset-binary `u64` of a PDU count, so key order matches signed value order
25/// (backfilled negatives sort below normal positives).
26#[must_use]
27pub fn bias_count(count: [u8; 8]) -> u64 {
28	i64::from_be_bytes(count)
29		.wrapping_sub(i64::MIN)
30		.cast_unsigned()
31}
32
33#[implement(super::Service)]
34pub async fn delete_pdus(&self, room_id: &RoomId) -> Result {
35	let current = self
36		.count_to_id(room_id, PduCount::min(), Direction::Forward)
37		.await?;
38
39	let prefix = current.shortroomid();
40	self.db
41		.pduid_pdu
42		.raw_stream_from(&current)
43		.ready_try_take_while(move |(key, _)| Ok(key.starts_with(&prefix)))
44		.ready_try_for_each(move |(key, value)| {
45			let pdu = serde_json::from_slice::<PduEvent>(value)?;
46			let ts: u64 = pdu.origin_server_ts.into();
47			let event_id = &pdu.event_id;
48
49			let mut txn = self.db.db.txn();
50
51			txn.del_raw(&self.db.pduid_pdu, key);
52			txn.del_raw(&self.db.eventid_pduid, event_id);
53			txn.del_raw(&self.db.eventid_outlierpdu, event_id);
54
55			let room_id_ts_key = (room_id, ts, bias_count(RawPduId::from(key).count()));
56			txn.del(&self.db.roomid_tscount_pducount, room_id_ts_key);
57
58			txn.execute();
59
60			trace!(?event_id, ?room_id, ?ts, ?key, "Removed");
61
62			Ok(())
63		})
64		.await
65}
66
67#[implement(super::Service)]
68pub fn pdus_near_ts(
69	&self,
70	user_id: Option<&UserId>,
71	room_id: &RoomId,
72	ts: MilliSecondsSinceUnixEpoch,
73	dir: Direction,
74) -> impl Stream<Item = Result<PdusIterItem>> + Send {
75	self.pdu_ids_near_ts(room_id, ts, dir)
76		.map_ok(|(ts, pdu_id)| (ts, pdu_id.into()))
77		.wide_and_then(async |(_, pdu_id): (_, RawPduId)| {
78			self.get_pdu_from_id(&pdu_id)
79				.map_ok(|pdu| (pdu_id, pdu))
80				.await
81		})
82		.ready_and_then(move |item| Self::each_pdu(item, user_id))
83}
84
85#[implement(super::Service)]
86pub fn pdu_ids_near_ts(
87	&self,
88	room_id: &RoomId,
89	ts: MilliSecondsSinceUnixEpoch,
90	dir: Direction,
91) -> impl Stream<Item = Result<(MilliSecondsSinceUnixEpoch, PduId)>> + Send {
92	use Direction::{Backward, Forward};
93
94	type KeyVal<'a> = ((&'a RoomId, UInt, u64), i64);
95
96	let ts: u64 = ts.get().into();
97
98	self.services
99		.short
100		.get_shortroomid(room_id)
101		.map_err(|e| err!(Request(NotFound("Room not found: {e:?}"))))
102		.map_ok(move |shortroomid| {
103			match dir {
104				| Forward => Left(self.db.roomid_tscount_pducount.stream_from(&(
105					room_id,
106					ts,
107					u64::MIN,
108				))),
109				| Backward => Right(self.db.roomid_tscount_pducount.rev_stream_from(&(
110					room_id,
111					ts,
112					u64::MAX,
113				))),
114			}
115			.ready_try_take_while(
116				move |((room_id_, ..), _): &KeyVal<'_>| Ok(room_id == *room_id_),
117			)
118			.map_ok(move |((_, ts, _), count)| {
119				(MilliSecondsSinceUnixEpoch(ts), PduId { shortroomid, count: count.into() })
120			})
121		})
122		.try_flatten_stream()
123}
124
125/// Returns an iterator over all PDUs in a room. Unknown rooms produce no
126/// items.
127#[implement(super::Service)]
128#[inline]
129pub fn all_pdus<'a>(
130	&'a self,
131	user_id: &'a UserId,
132	room_id: &'a RoomId,
133) -> impl Stream<Item = PdusIterItem> + Send + 'a {
134	self.pdus(Some(user_id), room_id, None)
135		.ignore_err()
136}
137
138/// Returns an iterator over all events and their tokens in a room that
139/// happened after the event with id `from` in order.
140#[implement(super::Service)]
141#[tracing::instrument(skip(self), level = "debug")]
142pub fn pdus<'a>(
143	&'a self,
144	user_id: Option<&'a UserId>,
145	room_id: &'a RoomId,
146	from: Option<PduCount>,
147) -> impl Stream<Item = Result<PdusIterItem>> + Send + 'a {
148	let from = from.unwrap_or_else(PduCount::min);
149	self.count_to_id(room_id, from, Direction::Forward)
150		.map_ok(move |current| {
151			let prefix = current.shortroomid();
152			self.db
153				.pduid_pdu
154				.raw_stream_from(&current)
155				.ready_try_take_while(move |(key, _)| Ok(key.starts_with(&prefix)))
156				.ready_and_then(move |item| Self::each_slice(item, user_id))
157		})
158		.try_flatten_stream()
159}
160
161/// Returns an iterator over all events and their tokens in a room that
162/// happened before the event with id `until` in reverse-order.
163#[implement(super::Service)]
164#[tracing::instrument(skip(self), level = "debug")]
165pub fn pdus_rev<'a>(
166	&'a self,
167	user_id: Option<&'a UserId>,
168	room_id: &'a RoomId,
169	until: Option<PduCount>,
170) -> impl Stream<Item = Result<PdusIterItem>> + Send + 'a {
171	let until = until.unwrap_or_else(PduCount::max);
172	self.count_to_id(room_id, until, Direction::Backward)
173		.map_ok(move |current| {
174			let prefix = current.shortroomid();
175			self.db
176				.pduid_pdu
177				.rev_raw_stream_from(&current)
178				.ready_try_take_while(move |(key, _)| Ok(key.starts_with(&prefix)))
179				.ready_and_then(move |item| Self::each_slice(item, user_id))
180		})
181		.try_flatten_stream()
182}
183
184#[implement(super::Service)]
185pub fn pdus_raw(&self) -> impl Stream<Item = Result<Val<'_>>> + Send {
186	self.db.pduid_pdu.raw_stream().map_ok(at!(1))
187}
188
189#[implement(super::Service)]
190pub fn outlier_pdus_raw(&self) -> impl Stream<Item = Result<Val<'_>>> + Send {
191	self.db
192		.eventid_outlierpdu
193		.raw_stream()
194		.map_ok(at!(1))
195}
196
197#[implement(super::Service)]
198fn each_slice((pdu_id, pdu): KeyVal<'_>, user_id: Option<&UserId>) -> Result<PdusIterItem> {
199	let pdu_id: RawPduId = pdu_id.into();
200	let pdu = serde_json::from_slice::<PduEvent>(pdu)?;
201
202	Self::each_pdu((pdu_id, pdu), user_id)
203}
204
205#[implement(super::Service)]
206fn each_pdu(
207	(pdu_id, mut pdu): (RawPduId, PduEvent),
208	user_id: Option<&UserId>,
209) -> Result<PdusIterItem> {
210	if Some(pdu.sender.borrow()) != user_id {
211		pdu.remove_transaction_id().log_err().ok();
212	}
213
214	pdu.add_age().log_err().ok();
215
216	Ok((pdu_id.pdu_count(), pdu))
217}