Files
continuwuity/src/service/rooms/state/mod.rs
T

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

454 lines
13 KiB
Rust
Raw Normal View History

2022-10-05 20:34:31 +02:00
use std::{
collections::{HashMap, HashSet},
fmt::Write,
2024-10-04 03:40:00 +00:00
iter::once,
2022-10-05 20:34:31 +02:00
sync::Arc,
};
2022-09-06 23:15:09 +02:00
use conduit::{
2024-11-22 16:01:27 +00:00
at, err,
result::FlatOk,
2024-10-15 09:34:43 +00:00
utils::{calculate_hash, stream::TryIgnore, IterStream, MutexMap, MutexMapGuard, ReadyExt},
2024-08-08 17:18:30 +00:00
warn, PduEvent, Result,
};
2024-10-15 09:34:43 +00:00
use database::{Deserialized, Ignore, Interfix, Map};
2024-10-04 03:40:00 +00:00
use futures::{future::join_all, pin_mut, FutureExt, Stream, StreamExt, TryFutureExt, TryStreamExt};
2022-10-05 20:34:31 +02:00
use ruma::{
events::{
room::{create::RoomCreateEventContent, member::RoomMemberEventContent},
2023-02-26 16:29:06 +01:00
AnyStrippedStateEvent, StateEventType, TimelineEventType,
2022-10-05 20:34:31 +02:00
},
serde::Raw,
state_res::{self, StateMap},
EventId, OwnedEventId, OwnedRoomId, RoomId, RoomVersionId, UserId,
2022-10-05 20:34:31 +02:00
};
2024-11-15 22:23:42 +00:00
use crate::{
globals, rooms,
rooms::{
short::{ShortEventId, ShortStateHash},
2024-11-17 03:57:21 +00:00
state_compressor::{parse_compressed_state_event, CompressedStateEvent},
2024-11-15 22:23:42 +00:00
},
Dep,
};
2024-05-09 15:59:08 -07:00
pub struct Service {
2024-10-15 09:34:43 +00:00
pub mutex: RoomMutexMap,
2024-07-18 06:37:47 +00:00
services: Services,
2024-06-28 22:51:39 +00:00
db: Data,
2022-06-20 11:31:27 +02:00
}
2024-07-18 06:37:47 +00:00
struct Services {
globals: Dep<globals::Service>,
short: Dep<rooms::short::Service>,
spaces: Dep<rooms::spaces::Service>,
state_cache: Dep<rooms::state_cache::Service>,
state_accessor: Dep<rooms::state_accessor::Service>,
state_compressor: Dep<rooms::state_compressor::Service>,
timeline: Dep<rooms::timeline::Service>,
}
2024-10-15 09:34:43 +00:00
struct Data {
shorteventid_shortstatehash: Arc<Map>,
roomid_shortstatehash: Arc<Map>,
roomid_pduleaves: Arc<Map>,
}
type RoomMutexMap = MutexMap<OwnedRoomId, ()>;
pub type RoomMutexGuard = MutexMapGuard<OwnedRoomId, ()>;
2024-07-04 03:26:19 +00:00
impl crate::Service for Service {
fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
Ok(Arc::new(Self {
2024-10-15 09:34:43 +00:00
mutex: RoomMutexMap::new(),
2024-07-18 06:37:47 +00:00
services: Services {
globals: args.depend::<globals::Service>("globals"),
short: args.depend::<rooms::short::Service>("rooms::short"),
spaces: args.depend::<rooms::spaces::Service>("rooms::spaces"),
state_cache: args.depend::<rooms::state_cache::Service>("rooms::state_cache"),
state_accessor: args.depend::<rooms::state_accessor::Service>("rooms::state_accessor"),
state_compressor: args.depend::<rooms::state_compressor::Service>("rooms::state_compressor"),
timeline: args.depend::<rooms::timeline::Service>("rooms::timeline"),
},
2024-10-15 09:34:43 +00:00
db: Data {
shorteventid_shortstatehash: args.db["shorteventid_shortstatehash"].clone(),
roomid_shortstatehash: args.db["roomid_shortstatehash"].clone(),
roomid_pduleaves: args.db["roomid_pduleaves"].clone(),
},
2024-07-04 03:26:19 +00:00
}))
2024-05-27 03:17:20 +00:00
}
2024-10-15 09:34:43 +00:00
fn memory_usage(&self, out: &mut dyn Write) -> Result {
let mutex = self.mutex.len();
writeln!(out, "state_mutex: {mutex}")?;
Ok(())
}
2024-07-04 03:26:19 +00:00
fn name(&self) -> &str { crate::service::make_name(std::module_path!()) }
}
impl Service {
2022-06-20 12:08:58 +02:00
/// Set the room to the given statehash and update caches.
2024-05-09 15:59:08 -07:00
pub async fn force_state(
2020-09-13 22:24:36 +02:00
&self,
room_id: &RoomId,
2022-06-20 12:08:58 +02:00
shortstatehash: u64,
statediffnew: Arc<HashSet<CompressedStateEvent>>,
_statediffremoved: Arc<HashSet<CompressedStateEvent>>,
2024-07-09 20:04:43 +00:00
state_lock: &RoomMutexGuard, // Take mutex guard to make sure users get the room state mutex
2024-10-15 09:34:43 +00:00
) -> Result {
2024-11-17 03:57:21 +00:00
let event_ids = statediffnew
.iter()
.stream()
.map(|&new| parse_compressed_state_event(new).1)
.then(|shorteventid| {
self.services
.short
.get_eventid_from_short::<Box<_>>(shorteventid)
})
2024-11-17 03:57:21 +00:00
.ignore_err();
2024-08-08 17:18:30 +00:00
pin_mut!(event_ids);
while let Some(event_id) = event_ids.next().await {
let Ok(pdu) = self.services.timeline.get_pdu(&event_id).await else {
continue;
2021-10-13 10:16:45 +02:00
};
2024-03-05 19:48:54 -05:00
2023-07-02 22:50:50 +02:00
match pdu.kind {
TimelineEventType::RoomMember => {
let Some(user_id) = pdu.state_key.as_ref().map(UserId::parse).flat_ok() else {
continue;
2023-07-02 22:50:50 +02:00
};
2024-03-05 19:48:54 -05:00
let Ok(membership_event) = pdu.get_content::<RoomMemberEventContent>() else {
continue;
2023-07-02 22:50:50 +02:00
};
2024-03-05 19:48:54 -05:00
2024-08-08 17:18:30 +00:00
self.services
.state_cache
.update_membership(room_id, &user_id, membership_event, &pdu.sender, None, None, false)
.await?;
2023-07-02 22:50:50 +02:00
},
TimelineEventType::SpaceChild => {
2024-07-18 06:37:47 +00:00
self.services
2024-03-25 17:05:11 -04:00
.spaces
.roomid_spacehierarchy_cache
.lock()
.await
.remove(&pdu.room_id);
2023-07-02 22:50:50 +02:00
},
_ => continue,
2024-03-05 19:48:54 -05:00
}
2023-07-02 22:50:50 +02:00
}
2024-03-05 19:48:54 -05:00
2024-08-08 17:18:30 +00:00
self.services.state_cache.update_joined_count(room_id).await;
2024-03-05 19:48:54 -05:00
2024-10-15 09:34:43 +00:00
self.set_room_state(room_id, shortstatehash, state_lock);
2024-03-05 19:48:54 -05:00
2021-01-18 19:08:59 -05:00
Ok(())
}
2024-03-05 19:48:54 -05:00
2022-06-19 22:56:14 +02:00
/// Generates a new StateHash and associates it with the incoming event.
///
/// This adds all current state events (not including the incoming event)
/// to `stateid_pduid` and adds the incoming event to `eventid_statehash`.
2024-07-07 19:03:15 +00:00
#[tracing::instrument(skip(self, state_ids_compressed), level = "debug")]
2024-08-08 17:18:30 +00:00
pub async fn set_event_state(
&self, event_id: &EventId, room_id: &RoomId, state_ids_compressed: Arc<HashSet<CompressedStateEvent>>,
2024-11-15 22:23:42 +00:00
) -> Result<ShortStateHash> {
const KEY_LEN: usize = size_of::<ShortEventId>();
const VAL_LEN: usize = size_of::<ShortStateHash>();
2024-10-15 09:34:43 +00:00
2024-08-08 17:18:30 +00:00
let shorteventid = self
.services
.short
.get_or_create_shorteventid(event_id)
.await;
2024-03-05 19:48:54 -05:00
2024-10-15 09:34:43 +00:00
let previous_shortstatehash = self.get_room_shortstatehash(room_id).await;
2024-03-05 19:48:54 -05:00
let state_hash = calculate_hash(state_ids_compressed.iter().map(|s| &s[..]));
2024-03-05 19:48:54 -05:00
2024-07-18 06:37:47 +00:00
let (shortstatehash, already_existed) = self
.services
2024-03-25 17:05:11 -04:00
.short
2024-08-08 17:18:30 +00:00
.get_or_create_shortstatehash(&state_hash)
.await;
2024-03-05 19:48:54 -05:00
2021-08-11 19:15:38 +02:00
if !already_existed {
2024-08-08 17:18:30 +00:00
let states_parents = if let Ok(p) = previous_shortstatehash {
self.services
.state_compressor
.load_shortstatehash_info(p)
.await?
} else {
Vec::new()
};
2024-03-05 19:48:54 -05:00
2022-10-05 20:34:31 +02:00
let (statediffnew, statediffremoved) = if let Some(parent_stateinfo) = states_parents.last() {
2024-03-25 17:05:11 -04:00
let statediffnew: HashSet<_> = state_ids_compressed
.difference(&parent_stateinfo.full_state)
2024-03-25 17:05:11 -04:00
.copied()
.collect();
2024-03-05 19:48:54 -05:00
2024-03-25 17:05:11 -04:00
let statediffremoved: HashSet<_> = parent_stateinfo
.full_state
2024-03-25 17:05:11 -04:00
.difference(&state_ids_compressed)
.copied()
.collect();
2024-03-05 19:48:54 -05:00
2021-10-13 11:51:30 +02:00
(Arc::new(statediffnew), Arc::new(statediffremoved))
2024-03-05 19:48:54 -05:00
} else {
2021-10-13 11:51:30 +02:00
(state_ids_compressed, Arc::new(HashSet::new()))
2024-03-05 19:48:54 -05:00
};
2024-07-18 06:37:47 +00:00
self.services.state_compressor.save_state_from_diff(
2021-08-12 23:04:00 +02:00
shortstatehash,
statediffnew,
statediffremoved,
1_000_000, // high number because no state will be based on this one
2022-10-05 18:36:12 +02:00
states_parents,
2024-03-05 19:48:54 -05:00
)?;
}
2024-10-15 09:34:43 +00:00
self.db
.shorteventid_shortstatehash
2024-11-15 22:23:42 +00:00
.aput::<KEY_LEN, VAL_LEN, _, _>(shorteventid, shortstatehash);
2024-03-05 19:48:54 -05:00
2022-10-05 18:36:12 +02:00
Ok(shortstatehash)
2024-03-05 19:48:54 -05:00
}
/// Generates a new StateHash and associates it with the incoming event.
///
/// This adds all current state events (not including the incoming event)
2021-03-17 22:30:25 +01:00
/// to `stateid_pduid` and adds the incoming event to `eventid_statehash`.
2024-07-07 19:03:15 +00:00
#[tracing::instrument(skip(self, new_pdu), level = "debug")]
2024-08-08 17:18:30 +00:00
pub async fn append_to_state(&self, new_pdu: &PduEvent) -> Result<u64> {
2024-10-15 09:34:43 +00:00
const BUFSIZE: usize = size_of::<u64>();
2024-07-18 06:37:47 +00:00
let shorteventid = self
.services
2024-03-25 17:05:11 -04:00
.short
2024-08-08 17:18:30 +00:00
.get_or_create_shorteventid(&new_pdu.event_id)
.await;
2024-03-05 19:48:54 -05:00
2024-08-08 17:18:30 +00:00
let previous_shortstatehash = self.get_room_shortstatehash(&new_pdu.room_id).await;
2024-03-05 19:48:54 -05:00
2024-08-08 17:18:30 +00:00
if let Ok(p) = previous_shortstatehash {
2024-10-15 09:34:43 +00:00
self.db
.shorteventid_shortstatehash
.aput::<BUFSIZE, BUFSIZE, _, _>(shorteventid, p);
2021-08-12 23:04:00 +02:00
}
2024-03-05 19:48:54 -05:00
2020-09-12 21:30:07 +02:00
if let Some(state_key) = &new_pdu.state_key {
2024-08-08 17:18:30 +00:00
let states_parents = if let Ok(p) = previous_shortstatehash {
self.services
.state_compressor
.load_shortstatehash_info(p)
.await?
} else {
Vec::new()
};
2024-03-05 19:48:54 -05:00
2024-07-18 06:37:47 +00:00
let shortstatekey = self
.services
2024-03-25 17:05:11 -04:00
.short
2024-08-08 17:18:30 +00:00
.get_or_create_shortstatekey(&new_pdu.kind.to_string().into(), state_key)
.await;
2024-03-05 19:48:54 -05:00
2024-07-18 06:37:47 +00:00
let new = self
.services
2024-03-25 17:05:11 -04:00
.state_compressor
2024-08-08 17:18:30 +00:00
.compress_state_event(shortstatekey, &new_pdu.event_id)
.await;
2024-03-05 19:48:54 -05:00
2022-10-05 20:34:31 +02:00
let replaces = states_parents
.last()
2024-03-25 17:05:11 -04:00
.map(|info| {
info.full_state
2024-03-25 17:05:11 -04:00
.iter()
.find(|bytes| bytes.starts_with(&shortstatekey.to_be_bytes()))
})
2022-10-05 20:34:31 +02:00
.unwrap_or_default();
2024-03-05 19:48:54 -05:00
2022-10-05 20:34:31 +02:00
if Some(&new) == replaces {
return Ok(previous_shortstatehash.expect("must exist"));
2024-03-05 19:48:54 -05:00
}
2021-08-12 23:04:00 +02:00
// TODO: statehash with deterministic inputs
2024-07-18 06:37:47 +00:00
let shortstatehash = self.services.globals.next_count()?;
2024-03-05 19:48:54 -05:00
2021-08-12 23:04:00 +02:00
let mut statediffnew = HashSet::new();
statediffnew.insert(new);
2024-03-05 19:48:54 -05:00
2021-08-12 23:04:00 +02:00
let mut statediffremoved = HashSet::new();
if let Some(replaces) = replaces {
2021-08-12 23:04:00 +02:00
statediffremoved.insert(*replaces);
2024-03-05 19:48:54 -05:00
}
2024-07-18 06:37:47 +00:00
self.services.state_compressor.save_state_from_diff(
2021-08-12 23:04:00 +02:00
shortstatehash,
Arc::new(statediffnew),
Arc::new(statediffremoved),
2,
states_parents,
)?;
2024-03-05 19:48:54 -05:00
2021-03-17 22:30:25 +01:00
Ok(shortstatehash)
2020-09-12 21:30:07 +02:00
} else {
2021-08-12 23:04:00 +02:00
Ok(previous_shortstatehash.expect("first event in room must be a state event"))
2024-03-05 19:48:54 -05:00
}
2020-09-12 21:30:07 +02:00
}
2024-03-05 19:48:54 -05:00
2024-10-04 03:40:00 +00:00
#[tracing::instrument(skip_all, level = "debug")]
pub async fn summary_stripped(&self, event: &PduEvent) -> Vec<Raw<AnyStrippedStateEvent>> {
2024-10-04 03:40:00 +00:00
let cells = [
(&StateEventType::RoomCreate, ""),
(&StateEventType::RoomJoinRules, ""),
(&StateEventType::RoomCanonicalAlias, ""),
(&StateEventType::RoomName, ""),
(&StateEventType::RoomAvatar, ""),
(&StateEventType::RoomMember, event.sender.as_str()), // Add recommended events
(&StateEventType::RoomEncryption, ""),
(&StateEventType::RoomTopic, ""),
2024-10-04 03:40:00 +00:00
];
let fetches = cells.iter().map(|(event_type, state_key)| {
self.services
.state_accessor
.room_state_get(&event.room_id, event_type, state_key)
2024-10-04 03:40:00 +00:00
});
2024-03-05 19:48:54 -05:00
2024-10-04 03:40:00 +00:00
join_all(fetches)
.await
.into_iter()
.filter_map(Result::ok)
.map(|e| e.to_stripped_state_event())
.chain(once(event.to_stripped_state_event()))
2024-10-04 03:40:00 +00:00
.collect()
2021-04-25 14:10:07 +02:00
}
2024-03-05 19:48:54 -05:00
/// Set the state hash to a new version, but does not update state_cache.
2024-10-15 09:34:43 +00:00
#[tracing::instrument(skip(self, _mutex_lock), level = "debug")]
2024-05-09 15:59:08 -07:00
pub fn set_room_state(
2022-10-05 20:34:31 +02:00
&self,
room_id: &RoomId,
shortstatehash: u64,
2024-10-15 09:34:43 +00:00
_mutex_lock: &RoomMutexGuard, // Take mutex guard to make sure users get the room state mutex
2024-08-08 17:18:30 +00:00
) {
2024-10-15 09:34:43 +00:00
const BUFSIZE: usize = size_of::<u64>();
self.db
.roomid_shortstatehash
.raw_aput::<BUFSIZE, _, _>(room_id, shortstatehash);
2020-12-31 14:52:08 +01:00
}
2024-03-05 19:48:54 -05:00
2022-09-06 23:15:09 +02:00
/// Returns the room's version.
2024-07-07 19:03:15 +00:00
#[tracing::instrument(skip(self), level = "debug")]
2024-08-08 17:18:30 +00:00
pub async fn get_room_version(&self, room_id: &RoomId) -> Result<RoomVersionId> {
self.services
2024-03-25 17:05:11 -04:00
.state_accessor
2024-08-08 17:18:30 +00:00
.room_state_get_content(room_id, &StateEventType::RoomCreate, "")
.await
.map(|content: RoomCreateEventContent| content.room_version)
.map_err(|e| err!(Request(NotFound("No create event found: {e:?}"))))
}
2024-03-05 19:48:54 -05:00
2024-11-15 22:23:42 +00:00
pub async fn get_room_shortstatehash(&self, room_id: &RoomId) -> Result<ShortStateHash> {
2024-10-15 09:34:43 +00:00
self.db
.roomid_shortstatehash
.get(room_id)
.await
.deserialized()
2022-09-07 13:25:51 +02:00
}
2024-03-05 19:48:54 -05:00
2024-08-08 17:18:30 +00:00
pub fn get_forward_extremities<'a>(&'a self, room_id: &'a RoomId) -> impl Stream<Item = &EventId> + Send + '_ {
let prefix = (room_id, Interfix);
self.db
.roomid_pduleaves
.keys_prefix(&prefix)
.map_ok(|(_, event_id): (Ignore, &EventId)| event_id)
.ignore_err()
2022-10-05 09:34:25 +02:00
}
2024-03-05 19:48:54 -05:00
2024-08-08 17:18:30 +00:00
pub async fn set_forward_extremities(
2022-10-05 20:34:31 +02:00
&self,
2022-10-05 20:33:55 +02:00
room_id: &RoomId,
2022-10-09 17:25:06 +02:00
event_ids: Vec<OwnedEventId>,
2024-10-15 09:34:43 +00:00
_state_lock: &RoomMutexGuard, // Take mutex guard to make sure users get the room state mutex
2024-08-08 17:18:30 +00:00
) {
2024-10-15 09:34:43 +00:00
let prefix = (room_id, Interfix);
2024-03-25 17:05:11 -04:00
self.db
2024-10-15 09:34:43 +00:00
.roomid_pduleaves
.keys_prefix_raw(&prefix)
.ignore_err()
.ready_for_each(|key| self.db.roomid_pduleaves.remove(key))
2024-08-08 17:18:30 +00:00
.await;
2024-10-15 09:34:43 +00:00
for event_id in &event_ids {
let key = (room_id, event_id);
self.db.roomid_pduleaves.put_raw(key, event_id);
}
2022-10-05 20:33:55 +02:00
}
2024-03-05 19:48:54 -05:00
2022-10-05 18:36:12 +02:00
/// This fetches auth events from the current state.
2024-07-07 19:03:15 +00:00
#[tracing::instrument(skip(self), level = "debug")]
2024-08-08 17:18:30 +00:00
pub async fn get_auth_events(
2023-02-26 16:29:06 +01:00
&self, room_id: &RoomId, kind: &TimelineEventType, sender: &UserId, state_key: Option<&str>,
2022-10-05 18:36:12 +02:00
content: &serde_json::value::RawValue,
) -> Result<StateMap<Arc<PduEvent>>> {
2024-08-08 17:18:30 +00:00
let Ok(shortstatehash) = self.get_room_shortstatehash(room_id).await else {
return Ok(HashMap::new());
};
2024-03-05 19:48:54 -05:00
2024-11-22 16:01:27 +00:00
let mut sauthevents: HashMap<_, _> = state_res::auth_types_for_event(kind, sender, state_key, content)?
2024-08-08 17:18:30 +00:00
.iter()
.stream()
2022-10-05 18:36:12 +02:00
.filter_map(|(event_type, state_key)| {
2024-07-18 06:37:47 +00:00
self.services
2022-10-05 20:34:31 +02:00
.short
2024-08-08 17:18:30 +00:00
.get_shortstatekey(event_type, state_key)
2024-11-22 16:01:27 +00:00
.map_ok(move |ssk| (ssk, (event_type, state_key)))
2024-08-08 17:18:30 +00:00
.map(Result::ok)
2022-10-05 18:36:12 +02:00
})
2024-11-22 16:01:27 +00:00
.map(|(ssk, (event_type, state_key))| (ssk, (event_type.to_owned(), state_key.to_owned())))
2024-08-08 17:18:30 +00:00
.collect()
.await;
2024-03-05 19:48:54 -05:00
2024-11-22 16:01:27 +00:00
let auth_state: Vec<_> = self
2024-07-18 06:37:47 +00:00
.services
2024-11-22 16:01:27 +00:00
.state_accessor
.state_full_shortids(shortstatehash)
2024-08-08 17:18:30 +00:00
.await
2024-11-22 16:01:27 +00:00
.map_err(|e| err!(Database(error!(?room_id, ?shortstatehash, "{e:?}"))))?
.into_iter()
.filter_map(|(shortstatekey, shorteventid)| {
sauthevents
.remove(&shortstatekey)
.map(|(event_type, state_key)| ((event_type, state_key), shorteventid))
})
.collect();
2024-08-08 17:18:30 +00:00
2024-11-22 16:01:27 +00:00
let auth_pdus: Vec<_> = self
.services
.short
.multi_get_eventid_from_short(auth_state.iter().map(at!(1)))
.await
.into_iter()
.stream()
.and_then(|event_id: OwnedEventId| async move { self.services.timeline.get_pdu(&event_id).await })
2024-11-22 16:01:27 +00:00
.collect()
.await;
2024-08-08 17:18:30 +00:00
2024-11-22 16:01:27 +00:00
let auth_pdus = auth_state
.into_iter()
.map(at!(0))
.zip(auth_pdus.into_iter())
.filter_map(|((event_type, state_key), pdu)| Some(((event_type, state_key), pdu.ok()?)))
.collect();
2024-08-08 17:18:30 +00:00
2024-11-22 16:01:27 +00:00
Ok(auth_pdus)
2022-10-05 18:36:12 +02:00
}
2022-06-20 11:31:27 +02:00
}