tuwunel_service/rooms/short/
mod.rs1use std::{borrow::Borrow, sync::Arc};
2
3use futures::{FutureExt, Stream, StreamExt, pin_mut};
4use ruma::{EventId, OwnedEventId, OwnedRoomId, RoomId, events::StateEventType};
5use serde::Deserialize;
6pub use tuwunel_core::matrix::{ShortEventId, ShortId, ShortRoomId, ShortStateKey};
7use tuwunel_core::{
8 Err, Result, err, implement,
9 matrix::StateKey,
10 utils,
11 utils::{
12 IterStream, MutexMap,
13 hash::sha256::Digest,
14 stream::{ReadyExt, WidebandExt},
15 },
16};
17use tuwunel_database::{Deserialized, Get, Map, Qry, Txn};
18
19pub struct Service {
20 db: Data,
21 creating: Creating,
22 services: Arc<crate::services::OnceServices>,
23}
24
25struct Data {
26 eventid_shorteventid: Arc<Map>,
27 shorteventid_eventid: Arc<Map>,
28 statekey_shortstatekey: Arc<Map>,
29 shortstatekey_statekey: Arc<Map>,
30 roomid_shortroomid: Arc<Map>,
31 statehash_shortstatehash: Arc<Map>,
32}
33
34#[derive(Default)]
41struct Creating {
42 shorteventid: MutexMap<OwnedEventId, ()>,
43 shortstatekey: MutexMap<(StateEventType, StateKey), ()>,
44 shortstatehash: MutexMap<Digest, ()>,
45 shortroomid: MutexMap<OwnedRoomId, ()>,
46}
47
48pub type ShortStateHash = ShortId;
49
50impl crate::Service for Service {
51 fn build(args: &crate::Args<'_>) -> Result<Arc<Self>> {
52 Ok(Arc::new(Self {
53 db: Data {
54 eventid_shorteventid: args.db["eventid_shorteventid"].clone(),
55 shorteventid_eventid: args.db["shorteventid_eventid"].clone(),
56 statekey_shortstatekey: args.db["statekey_shortstatekey"].clone(),
57 shortstatekey_statekey: args.db["shortstatekey_statekey"].clone(),
58 roomid_shortroomid: args.db["roomid_shortroomid"].clone(),
59 statehash_shortstatehash: args.db["statehash_shortstatehash"].clone(),
60 },
61 creating: Creating::default(),
62 services: args.services.clone(),
63 }))
64 }
65
66 fn name(&self) -> &str { crate::service::make_name(std::module_path!()) }
67}
68
69#[implement(Service)]
70pub async fn get_or_create_shorteventid(&self, event_id: &EventId) -> ShortEventId {
71 if let Ok(shorteventid) = self.get_shorteventid(event_id).await {
72 return shorteventid;
73 }
74
75 self.create_shorteventid(event_id).await
76}
77
78#[implement(Service)]
84pub fn multi_get_or_create_shorteventid<'a, I>(
85 &'a self,
86 event_ids: I,
87) -> impl Stream<Item = ShortEventId> + Send + '_
88where
89 I: Iterator<Item = &'a EventId> + Clone + Send + 'a,
90{
91 event_ids
92 .clone()
93 .stream()
94 .get(&self.db.eventid_shorteventid)
95 .zip(event_ids.into_iter().stream())
96 .wide_then(async |(result, event_id)| match result {
97 | Ok(ref short) => utils::u64_from_u8(short),
98 | Err(_) => self.create_shorteventid(event_id).await,
99 })
100}
101
102#[implement(Service)]
103async fn create_shorteventid(&self, event_id: &EventId) -> ShortEventId {
104 let _lock = self.creating.shorteventid.lock(event_id).await;
105
106 if let Ok(shorteventid) = self.get_shorteventid(event_id).await {
107 return shorteventid;
108 }
109
110 let short = self.services.globals.next_count();
111 let mut txn = self.services.db.txn();
112
113 txn.insert_raw(&self.db.shorteventid_eventid, (*short).to_be_bytes(), event_id);
114 txn.insert_raw(&self.db.eventid_shorteventid, event_id, (*short).to_be_bytes());
115 txn.execute();
116
117 *short
118}
119
120#[implement(Service)]
121pub async fn get_shorteventid(&self, event_id: &EventId) -> Result<ShortEventId> {
122 self.db
123 .eventid_shorteventid
124 .get(event_id)
125 .await
126 .deserialized()
127}
128
129#[implement(Service)]
130pub async fn get_or_create_shortstatekey(
131 &self,
132 event_type: &StateEventType,
133 state_key: &str,
134) -> ShortStateKey {
135 if let Ok(shortstatekey) = self
136 .get_shortstatekey(event_type, state_key)
137 .await
138 {
139 return shortstatekey;
140 }
141
142 self.create_shortstatekey(event_type, state_key)
143 .await
144}
145
146#[implement(Service)]
147async fn create_shortstatekey(
148 &self,
149 event_type: &StateEventType,
150 state_key: &str,
151) -> ShortStateKey {
152 let owned_key = (event_type.clone(), StateKey::from_str(state_key));
153 let _lock = self.creating.shortstatekey.lock(&owned_key).await;
154
155 if let Ok(shortstatekey) = self
156 .get_shortstatekey(event_type, state_key)
157 .await
158 {
159 return shortstatekey;
160 }
161
162 let key = (event_type, state_key);
163 let shortstatekey = self.services.globals.next_count();
164 let mut txn = self.services.db.txn();
165
166 txn.put(&self.db.shortstatekey_statekey, *shortstatekey, key);
167 txn.put(&self.db.statekey_shortstatekey, key, *shortstatekey);
168 txn.execute();
169
170 *shortstatekey
171}
172
173#[implement(Service)]
174pub async fn get_shortstatekey(
175 &self,
176 event_type: &StateEventType,
177 state_key: &str,
178) -> Result<ShortStateKey> {
179 let key = (event_type, state_key);
180 self.db
181 .statekey_shortstatekey
182 .qry(&key)
183 .await
184 .deserialized()
185}
186
187#[implement(Service)]
188pub async fn get_eventid_from_short<Id>(&self, shorteventid: ShortEventId) -> Result<Id>
189where
190 Id: for<'de> Deserialize<'de> + Send + Sized + ToOwned,
191 <Id as ToOwned>::Owned: Borrow<EventId>,
192{
193 const BUFSIZE: usize = size_of::<ShortEventId>();
194
195 self.db
196 .shorteventid_eventid
197 .aqry::<BUFSIZE, _>(&shorteventid)
198 .await
199 .deserialized()
200 .map_err(|e| err!(Database("Failed to find EventId from short {shorteventid:?}: {e:?}")))
201}
202
203#[implement(Service)]
204pub fn multi_get_eventid_from_short<'a, Id, S>(
205 &'a self,
206 shorteventid: S,
207) -> impl Stream<Item = Result<Id>> + Send + 'a
208where
209 S: Stream<Item = ShortEventId> + Send + 'a,
210 Id: for<'de> Deserialize<'de> + Send + Sized + ToOwned + 'a,
211 <Id as ToOwned>::Owned: Borrow<EventId>,
212{
213 shorteventid
214 .qry(&self.db.shorteventid_eventid)
215 .map(Deserialized::deserialized)
216}
217
218#[implement(Service)]
219pub async fn get_statekey_from_short(
220 &self,
221 shortstatekey: ShortStateKey,
222) -> Result<(StateEventType, StateKey)> {
223 const BUFSIZE: usize = size_of::<ShortStateKey>();
224
225 self.db
226 .shortstatekey_statekey
227 .aqry::<BUFSIZE, _>(&shortstatekey)
228 .await
229 .deserialized()
230 .map_err(|e| {
231 err!(Database(
232 "Failed to find (StateEventType, state_key) from short {shortstatekey:?}: {e:?}"
233 ))
234 })
235}
236
237#[implement(Service)]
238pub fn multi_get_statekey_from_short<'a, S>(
239 &'a self,
240 shortstatekey: S,
241) -> impl Stream<Item = Result<(StateEventType, StateKey)>> + Send + 'a
242where
243 S: Stream<Item = ShortStateKey> + Send + 'a,
244{
245 shortstatekey
246 .qry(&self.db.shortstatekey_statekey)
247 .map(Deserialized::deserialized)
248}
249
250#[implement(Service)]
252pub async fn get_or_create_shortstatehash<F>(
253 &self,
254 state_hash: &Digest,
255 write_statediff: F,
256) -> Result<(ShortStateHash, bool)>
257where
258 F: FnOnce(&mut Txn, ShortStateHash) -> Result,
259{
260 if let Ok(shortstatehash) = self.get_shortstatehash(state_hash).await {
261 return Ok((shortstatehash, true));
262 }
263
264 self.create_shortstatehash(state_hash, write_statediff)
265 .await
266}
267
268#[implement(Service)]
269async fn create_shortstatehash<F>(
270 &self,
271 state_hash: &Digest,
272 write_statediff: F,
273) -> Result<(ShortStateHash, bool)>
274where
275 F: FnOnce(&mut Txn, ShortStateHash) -> Result,
276{
277 let _lock = self
278 .creating
279 .shortstatehash
280 .lock(state_hash)
281 .await;
282
283 if let Ok(shortstatehash) = self.get_shortstatehash(state_hash).await {
284 return Ok((shortstatehash, true));
285 }
286
287 let shortstatehash = self.services.globals.next_count();
288 let mut txn = self.services.db.txn();
289
290 txn.insert_raw(
291 &self.db.statehash_shortstatehash,
292 state_hash,
293 (*shortstatehash).to_be_bytes(),
294 );
295 write_statediff(&mut txn, *shortstatehash)?;
296 txn.execute();
297
298 Ok((*shortstatehash, false))
299}
300
301#[implement(Service)]
302pub async fn get_shortstatehash(&self, state_hash: &Digest) -> Result<ShortStateHash> {
303 self.db
304 .statehash_shortstatehash
305 .get(state_hash)
306 .await
307 .deserialized()
308}
309
310#[implement(Service)]
311pub async fn get_shortroomid(&self, room_id: &RoomId) -> Result<ShortRoomId> {
312 self.db
313 .roomid_shortroomid
314 .get(room_id)
315 .await
316 .deserialized()
317}
318
319#[implement(Service)]
320pub async fn get_roomid_from_short(&self, shortroomid_: ShortRoomId) -> Result<OwnedRoomId> {
321 let stream = self
322 .db
323 .roomid_shortroomid
324 .stream()
325 .ready_filter_map(Result::ok);
326
327 pin_mut!(stream);
328 stream
329 .ready_find(|&(_, shortroomid)| shortroomid == shortroomid_)
330 .map(|found| found.map(|(room_id, _): (&RoomId, ShortRoomId)| room_id.to_owned()))
331 .await
332 .ok_or_else(|| err!(Database("Failed to find RoomId from {shortroomid_:?}")))
333}
334
335#[implement(Service)]
336pub async fn get_or_create_shortroomid(&self, room_id: &RoomId) -> ShortRoomId {
337 if let Ok(shortroomid) = self.get_shortroomid(room_id).await {
338 return shortroomid;
339 }
340
341 self.create_shortroomid(room_id).await
342}
343
344#[implement(Service)]
345async fn create_shortroomid(&self, room_id: &RoomId) -> ShortRoomId {
346 const BUFSIZE: usize = size_of::<ShortRoomId>();
347
348 let _lock = self.creating.shortroomid.lock(room_id).await;
349
350 if let Ok(shortroomid) = self.get_shortroomid(room_id).await {
351 return shortroomid;
352 }
353
354 let short = self.services.globals.next_count();
355
356 debug_assert!(size_of_val(&*short) == BUFSIZE, "buffer requirement changed");
357
358 self.db
359 .roomid_shortroomid
360 .raw_aput::<BUFSIZE, _, _>(room_id, *short);
361
362 *short
363}
364
365#[implement(Service)]
366pub async fn delete_shortroomid(&self, room_id: &RoomId) -> Result {
367 if self
368 .db
369 .roomid_shortroomid
370 .exists(room_id)
371 .await
372 .is_ok()
373 {
374 self.db.roomid_shortroomid.remove(room_id);
375 Ok(())
376 } else {
377 Err!(Database("not found"))
378 }
379}