tuwunel_service/rooms/timeline/
pdus.rs1use 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#[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(¤t)
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#[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#[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(¤t)
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#[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(¤t)
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}