1
0
Fork 0
mirror of https://forgejo.ellis.link/continuwuation/continuwuity.git synced 2025-07-31 12:18:31 +00:00
continuwuity/src/service/rooms/auth_chain/mod.rs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

184 lines
4.7 KiB
Rust
Raw Normal View History

mod data;
2022-10-05 20:34:31 +02:00
use std::{
collections::{BTreeSet, HashSet},
sync::Arc,
};
pub(crate) use data::Data;
2022-10-05 20:34:31 +02:00
use ruma::{api::client::error::ErrorKind, EventId, RoomId};
use tracing::{debug, error, warn};
2021-08-12 23:04:00 +02:00
2022-10-05 20:34:31 +02:00
use crate::{services, Error, Result};
2021-08-14 19:07:50 +02:00
pub(crate) struct Service {
pub(crate) db: &'static dyn Data,
}
2022-06-20 12:08:58 +02:00
2022-10-05 12:45:54 +02:00
impl Service {
pub(crate) async fn event_ids_iter<'a>(
&self, room_id: &RoomId, starting_events_: Vec<Arc<EventId>>,
2022-10-05 18:36:12 +02:00
) -> Result<impl Iterator<Item = Arc<EventId>> + 'a> {
let mut starting_events: Vec<&EventId> = Vec::with_capacity(starting_events_.len());
for starting_event in &starting_events_ {
starting_events.push(starting_event);
}
Ok(self
.get_auth_chain(room_id, &starting_events)
.await?
.into_iter()
.filter_map(move |sid| services().rooms.short.get_eventid_from_short(sid).ok()))
}
pub(crate) async fn get_auth_chain(&self, room_id: &RoomId, starting_events: &[&EventId]) -> Result<Vec<u64>> {
const NUM_BUCKETS: usize = 50; //TODO: change possible w/o disrupting db?
const BUCKET: BTreeSet<(u64, &EventId)> = BTreeSet::new();
let started = std::time::Instant::now();
let mut buckets = [BUCKET; NUM_BUCKETS];
for (i, short) in services()
.rooms
.short
.multi_get_or_create_shorteventid(starting_events)?
.iter()
.enumerate()
{
let bucket = short % NUM_BUCKETS as u64;
buckets[bucket as usize].insert((*short, starting_events[i]));
}
debug!(
starting_events = ?starting_events.len(),
elapsed = ?started.elapsed(),
"start",
);
2022-10-05 18:36:12 +02:00
let mut hits = 0;
let mut misses = 0;
let mut full_auth_chain = Vec::new();
2022-10-05 18:36:12 +02:00
for chunk in buckets {
if chunk.is_empty() {
continue;
}
2022-10-05 18:36:12 +02:00
let chunk_key: Vec<u64> = chunk.iter().map(|(short, _)| short).copied().collect();
if let Some(cached) = services()
.rooms
.auth_chain
.get_cached_eventid_authchain(&chunk_key)?
{
2022-10-05 18:36:12 +02:00
full_auth_chain.extend(cached.iter().copied());
hits += 1;
2022-10-05 18:36:12 +02:00
continue;
}
2022-10-05 18:36:12 +02:00
let mut hits2 = 0;
let mut misses2 = 0;
let mut chunk_cache = Vec::new();
2022-10-05 18:36:12 +02:00
for (sevent_id, event_id) in chunk {
if let Some(cached) = services()
.rooms
.auth_chain
.get_cached_eventid_authchain(&[sevent_id])?
{
2022-10-05 18:36:12 +02:00
chunk_cache.extend(cached.iter().copied());
hits2 += 1;
2022-10-05 18:36:12 +02:00
} else {
let auth_chain = self.get_auth_chain_inner(room_id, event_id)?;
services()
.rooms
.auth_chain
.cache_auth_chain(vec![sevent_id], &auth_chain)?;
chunk_cache.extend(auth_chain.iter());
misses2 += 1;
debug!(
event_id = ?event_id,
chain_length = ?auth_chain.len(),
chunk_cache_length = ?chunk_cache.len(),
elapsed = ?started.elapsed(),
"Cache missed event"
2022-10-05 18:36:12 +02:00
);
};
}
chunk_cache.sort_unstable();
chunk_cache.dedup();
services()
.rooms
.auth_chain
.cache_auth_chain_vec(chunk_key, &chunk_cache)?;
full_auth_chain.extend(chunk_cache.iter());
misses += 1;
debug!(
chunk_cache_length = ?chunk_cache.len(),
hits = ?hits2,
misses = ?misses2,
elapsed = ?started.elapsed(),
"Chunk missed",
2022-10-05 18:36:12 +02:00
);
}
full_auth_chain.sort();
full_auth_chain.dedup();
debug!(
chain_length = ?full_auth_chain.len(),
hits = ?hits,
misses = ?misses,
elapsed = ?started.elapsed(),
"done",
2022-10-05 18:36:12 +02:00
);
Ok(full_auth_chain)
2022-10-05 18:36:12 +02:00
}
2022-10-05 18:36:12 +02:00
#[tracing::instrument(skip(self, event_id))]
2022-10-05 20:34:31 +02:00
fn get_auth_chain_inner(&self, room_id: &RoomId, event_id: &EventId) -> Result<HashSet<u64>> {
2022-10-05 18:36:12 +02:00
let mut todo = vec![Arc::from(event_id)];
let mut found = HashSet::new();
2022-10-05 18:36:12 +02:00
while let Some(event_id) = todo.pop() {
match services().rooms.timeline.get_pdu(&event_id) {
Ok(Some(pdu)) => {
if pdu.room_id != room_id {
return Err(Error::BadRequest(ErrorKind::forbidden(), "Evil event in db"));
2022-10-05 18:36:12 +02:00
}
for auth_event in &pdu.auth_events {
let sauthevent = services()
.rooms
.short
.get_or_create_shorteventid(auth_event)?;
if found.insert(sauthevent) {
2022-10-05 18:36:12 +02:00
todo.push(auth_event.clone());
}
2022-10-05 18:36:12 +02:00
}
},
Ok(None) => {
warn!(?event_id, "Could not find pdu mentioned in auth events");
2022-10-05 18:36:12 +02:00
},
Err(error) => {
error!(?event_id, ?error, "Could not load event in auth chain");
},
2022-10-05 18:36:12 +02:00
}
}
2022-10-05 18:36:12 +02:00
Ok(found)
}
pub(crate) fn get_cached_eventid_authchain(&self, key: &[u64]) -> Result<Option<Arc<[u64]>>> {
self.db.get_cached_eventid_authchain(key)
}
#[tracing::instrument(skip(self))]
pub(crate) fn cache_auth_chain(&self, key: Vec<u64>, auth_chain: &HashSet<u64>) -> Result<()> {
self.db
.cache_auth_chain(key, auth_chain.iter().copied().collect::<Arc<[u64]>>())
}
#[tracing::instrument(skip(self))]
pub(crate) fn cache_auth_chain_vec(&self, key: Vec<u64>, auth_chain: &Vec<u64>) -> Result<()> {
self.db
.cache_auth_chain(key, auth_chain.iter().copied().collect::<Arc<[u64]>>())
}
}