use std::{ collections::{BTreeMap, HashMap}, path::Path, sync::{Arc, Mutex, RwLock}, }; use conduit::{Config, Error, PduCount, Result, Server}; use lru_cache::LruCache; use ruma::{CanonicalJsonValue, OwnedDeviceId, OwnedRoomId, OwnedUserId}; use tracing::debug; use crate::{KeyValueDatabaseEngine, KvTree}; pub struct KeyValueDatabase { pub db: Arc, //pub globals: globals::Globals, pub global: Arc, pub server_signingkeys: Arc, pub roomid_inviteviaservers: Arc, //pub users: users::Users, pub userid_password: Arc, pub userid_displayname: Arc, pub userid_avatarurl: Arc, pub userid_blurhash: Arc, pub userdeviceid_token: Arc, pub userdeviceid_metadata: Arc, // This is also used to check if a device exists pub userid_devicelistversion: Arc, // DevicelistVersion = u64 pub token_userdeviceid: Arc, pub onetimekeyid_onetimekeys: Arc, // OneTimeKeyId = UserId + DeviceKeyId pub userid_lastonetimekeyupdate: Arc, // LastOneTimeKeyUpdate = Count pub keychangeid_userid: Arc, // KeyChangeId = UserId/RoomId + Count pub keyid_key: Arc, // KeyId = UserId + KeyId (depends on key type) pub userid_masterkeyid: Arc, pub userid_selfsigningkeyid: Arc, pub userid_usersigningkeyid: Arc, pub userfilterid_filter: Arc, // UserFilterId = UserId + FilterId pub todeviceid_events: Arc, // ToDeviceId = UserId + DeviceId + Count pub userid_presenceid: Arc, // UserId => Count pub presenceid_presence: Arc, // Count + UserId => Presence //pub uiaa: uiaa::Uiaa, pub userdevicesessionid_uiaainfo: Arc, // User-interactive authentication pub userdevicesessionid_uiaarequest: RwLock>, //pub edus: RoomEdus, pub readreceiptid_readreceipt: Arc, // ReadReceiptId = RoomId + Count + UserId pub roomuserid_privateread: Arc, // RoomUserId = Room + User, PrivateRead = Count pub roomuserid_lastprivatereadupdate: Arc, // LastPrivateReadUpdate = Count //pub rooms: rooms::Rooms, pub pduid_pdu: Arc, // PduId = ShortRoomId + Count pub eventid_pduid: Arc, pub roomid_pduleaves: Arc, pub alias_roomid: Arc, pub alias_userid: Arc, // UserId = AliasId (User who created the alias) pub aliasid_alias: Arc, // AliasId = RoomId + Count pub publicroomids: Arc, pub threadid_userids: Arc, // ThreadId = RoomId + Count pub tokenids: Arc, // TokenId = ShortRoomId + Token + PduIdCount /// Participating servers in a room. pub roomserverids: Arc, // RoomServerId = RoomId + ServerName pub serverroomids: Arc, // ServerRoomId = ServerName + RoomId pub userroomid_joined: Arc, pub roomuserid_joined: Arc, pub roomid_joinedcount: Arc, pub roomid_invitedcount: Arc, pub roomuseroncejoinedids: Arc, pub userroomid_invitestate: Arc, // InviteState = Vec> pub roomuserid_invitecount: Arc, // InviteCount = Count pub userroomid_leftstate: Arc, pub roomuserid_leftcount: Arc, pub disabledroomids: Arc, // Rooms where incoming federation handling is disabled pub bannedroomids: Arc, // Rooms where local users are not allowed to join pub lazyloadedids: Arc, // LazyLoadedIds = UserId + DeviceId + RoomId + LazyLoadedUserId pub userroomid_notificationcount: Arc, // NotifyCount = u64 pub userroomid_highlightcount: Arc, // HightlightCount = u64 pub roomuserid_lastnotificationread: Arc, // LastNotificationRead = u64 /// Remember the current state hash of a room. pub roomid_shortstatehash: Arc, pub roomsynctoken_shortstatehash: Arc, /// Remember the state hash at events in the past. pub shorteventid_shortstatehash: Arc, pub statekey_shortstatekey: Arc, /* StateKey = EventType + StateKey, ShortStateKey = * Count */ pub shortstatekey_statekey: Arc, pub roomid_shortroomid: Arc, pub shorteventid_eventid: Arc, pub eventid_shorteventid: Arc, pub statehash_shortstatehash: Arc, pub shortstatehash_statediff: Arc, /* StateDiff = parent (or 0) + * (shortstatekey+shorteventid++) + 0_u64 + * (shortstatekey+shorteventid--) */ pub shorteventid_authchain: Arc, /// RoomId + EventId -> outlier PDU. /// Any pdu that has passed the steps 1-8 in the incoming event /// /federation/send/txn. pub eventid_outlierpdu: Arc, pub softfailedeventids: Arc, /// ShortEventId + ShortEventId -> (). pub tofrom_relation: Arc, /// RoomId + EventId -> Parent PDU EventId. pub referencedevents: Arc, //pub account_data: account_data::AccountData, pub roomuserdataid_accountdata: Arc, // RoomUserDataId = Room + User + Count + Type pub roomusertype_roomuserdataid: Arc, // RoomUserType = Room + User + Type //pub media: media::Media, pub mediaid_file: Arc, // MediaId = MXC + WidthHeight + ContentDisposition + ContentType pub url_previews: Arc, pub mediaid_user: Arc, //pub key_backups: key_backups::KeyBackups, pub backupid_algorithm: Arc, // BackupId = UserId + Version(Count) pub backupid_etag: Arc, // BackupId = UserId + Version(Count) pub backupkeyid_backup: Arc, // BackupKeyId = UserId + Version + RoomId + SessionId //pub transaction_ids: transaction_ids::TransactionIds, pub userdevicetxnid_response: Arc, /* Response can be empty (/sendToDevice) or the event id * (/send) */ //pub sending: sending::Sending, pub servername_educount: Arc, // EduCount: Count of last EDU sync pub servernameevent_data: Arc, /* ServernameEvent = (+ / $)SenderKey / ServerName / UserId + * PduId / Id (for edus), Data = EDU content */ pub servercurrentevent_data: Arc, /* ServerCurrentEvents = (+ / $)ServerName / UserId + PduId * / Id (for edus), Data = EDU content */ //pub appservice: appservice::Appservice, pub id_appserviceregistrations: Arc, //pub pusher: pusher::PushData, pub senderkey_pusher: Arc, pub auth_chain_cache: Mutex, Arc<[u64]>>>, pub appservice_in_room_cache: RwLock>>, pub lasttimelinecount_cache: Mutex>, } impl KeyValueDatabase { /// Load an existing database or create a new one. #[allow(clippy::too_many_lines)] pub async fn load_or_create(server: &Arc) -> Result { let config = &server.config; check_db_setup(config)?; let builder = build(config)?; Ok(Self { db: builder.clone(), userid_password: builder.open_tree("userid_password")?, userid_displayname: builder.open_tree("userid_displayname")?, userid_avatarurl: builder.open_tree("userid_avatarurl")?, userid_blurhash: builder.open_tree("userid_blurhash")?, userdeviceid_token: builder.open_tree("userdeviceid_token")?, userdeviceid_metadata: builder.open_tree("userdeviceid_metadata")?, userid_devicelistversion: builder.open_tree("userid_devicelistversion")?, token_userdeviceid: builder.open_tree("token_userdeviceid")?, onetimekeyid_onetimekeys: builder.open_tree("onetimekeyid_onetimekeys")?, userid_lastonetimekeyupdate: builder.open_tree("userid_lastonetimekeyupdate")?, keychangeid_userid: builder.open_tree("keychangeid_userid")?, keyid_key: builder.open_tree("keyid_key")?, userid_masterkeyid: builder.open_tree("userid_masterkeyid")?, userid_selfsigningkeyid: builder.open_tree("userid_selfsigningkeyid")?, userid_usersigningkeyid: builder.open_tree("userid_usersigningkeyid")?, userfilterid_filter: builder.open_tree("userfilterid_filter")?, todeviceid_events: builder.open_tree("todeviceid_events")?, userid_presenceid: builder.open_tree("userid_presenceid")?, presenceid_presence: builder.open_tree("presenceid_presence")?, userdevicesessionid_uiaainfo: builder.open_tree("userdevicesessionid_uiaainfo")?, userdevicesessionid_uiaarequest: RwLock::new(BTreeMap::new()), readreceiptid_readreceipt: builder.open_tree("readreceiptid_readreceipt")?, roomuserid_privateread: builder.open_tree("roomuserid_privateread")?, // "Private" read receipt roomuserid_lastprivatereadupdate: builder.open_tree("roomuserid_lastprivatereadupdate")?, pduid_pdu: builder.open_tree("pduid_pdu")?, eventid_pduid: builder.open_tree("eventid_pduid")?, roomid_pduleaves: builder.open_tree("roomid_pduleaves")?, alias_roomid: builder.open_tree("alias_roomid")?, alias_userid: builder.open_tree("alias_userid")?, aliasid_alias: builder.open_tree("aliasid_alias")?, publicroomids: builder.open_tree("publicroomids")?, threadid_userids: builder.open_tree("threadid_userids")?, tokenids: builder.open_tree("tokenids")?, roomserverids: builder.open_tree("roomserverids")?, serverroomids: builder.open_tree("serverroomids")?, userroomid_joined: builder.open_tree("userroomid_joined")?, roomuserid_joined: builder.open_tree("roomuserid_joined")?, roomid_joinedcount: builder.open_tree("roomid_joinedcount")?, roomid_invitedcount: builder.open_tree("roomid_invitedcount")?, roomuseroncejoinedids: builder.open_tree("roomuseroncejoinedids")?, userroomid_invitestate: builder.open_tree("userroomid_invitestate")?, roomuserid_invitecount: builder.open_tree("roomuserid_invitecount")?, userroomid_leftstate: builder.open_tree("userroomid_leftstate")?, roomuserid_leftcount: builder.open_tree("roomuserid_leftcount")?, disabledroomids: builder.open_tree("disabledroomids")?, bannedroomids: builder.open_tree("bannedroomids")?, lazyloadedids: builder.open_tree("lazyloadedids")?, userroomid_notificationcount: builder.open_tree("userroomid_notificationcount")?, userroomid_highlightcount: builder.open_tree("userroomid_highlightcount")?, roomuserid_lastnotificationread: builder.open_tree("userroomid_highlightcount")?, statekey_shortstatekey: builder.open_tree("statekey_shortstatekey")?, shortstatekey_statekey: builder.open_tree("shortstatekey_statekey")?, shorteventid_authchain: builder.open_tree("shorteventid_authchain")?, roomid_shortroomid: builder.open_tree("roomid_shortroomid")?, shortstatehash_statediff: builder.open_tree("shortstatehash_statediff")?, eventid_shorteventid: builder.open_tree("eventid_shorteventid")?, shorteventid_eventid: builder.open_tree("shorteventid_eventid")?, shorteventid_shortstatehash: builder.open_tree("shorteventid_shortstatehash")?, roomid_shortstatehash: builder.open_tree("roomid_shortstatehash")?, roomsynctoken_shortstatehash: builder.open_tree("roomsynctoken_shortstatehash")?, statehash_shortstatehash: builder.open_tree("statehash_shortstatehash")?, eventid_outlierpdu: builder.open_tree("eventid_outlierpdu")?, softfailedeventids: builder.open_tree("softfailedeventids")?, tofrom_relation: builder.open_tree("tofrom_relation")?, referencedevents: builder.open_tree("referencedevents")?, roomuserdataid_accountdata: builder.open_tree("roomuserdataid_accountdata")?, roomusertype_roomuserdataid: builder.open_tree("roomusertype_roomuserdataid")?, mediaid_file: builder.open_tree("mediaid_file")?, url_previews: builder.open_tree("url_previews")?, mediaid_user: builder.open_tree("mediaid_user")?, backupid_algorithm: builder.open_tree("backupid_algorithm")?, backupid_etag: builder.open_tree("backupid_etag")?, backupkeyid_backup: builder.open_tree("backupkeyid_backup")?, userdevicetxnid_response: builder.open_tree("userdevicetxnid_response")?, servername_educount: builder.open_tree("servername_educount")?, servernameevent_data: builder.open_tree("servernameevent_data")?, servercurrentevent_data: builder.open_tree("servercurrentevent_data")?, id_appserviceregistrations: builder.open_tree("id_appserviceregistrations")?, senderkey_pusher: builder.open_tree("senderkey_pusher")?, global: builder.open_tree("global")?, server_signingkeys: builder.open_tree("server_signingkeys")?, roomid_inviteviaservers: builder.open_tree("roomid_inviteviaservers")?, auth_chain_cache: Mutex::new(LruCache::new( (f64::from(config.auth_chain_cache_capacity) * config.conduit_cache_capacity_modifier) as usize, )), appservice_in_room_cache: RwLock::new(HashMap::new()), lasttimelinecount_cache: Mutex::new(HashMap::new()), }) } } fn build(config: &Config) -> Result> { match &*config.database_backend { "rocksdb" => { debug!("Got rocksdb database backend"); #[cfg(not(feature = "rocksdb"))] return Err(Error::bad_config("Database backend not found.")); #[cfg(feature = "rocksdb")] Ok(Arc::new(Arc::::open(config)?)) }, _ => Err(Error::bad_config( "Database backend not found. rocksdb is the only supported backend.", )), } } fn check_db_setup(config: &Config) -> Result<()> { let path = Path::new(&config.database_path); let rocksdb_exists = path.join("IDENTITY").exists(); if rocksdb_exists && config.database_backend != "rocksdb" { return Err(Error::bad_config( "Found rocksdb at database_path, but is not specified in config.", )); } Ok(()) }