improvement: feature flags for sled, rocksdb

next
Timo Kösters 2021-06-12 15:04:28 +02:00
parent 2078af59d8
commit cd4bc520d8
No known key found for this signature in database
GPG Key ID: 24DA7517711A2BA4
10 changed files with 71 additions and 50 deletions

View File

@ -24,8 +24,8 @@ ruma = { git = "https://github.com/ruma/ruma", rev = "b39537812c12caafcbf8b7bd74
# Used for long polling and federation sender, should be the same as rocket::tokio # Used for long polling and federation sender, should be the same as rocket::tokio
tokio = "1.2.0" tokio = "1.2.0"
# Used for storing data permanently # Used for storing data permanently
sled = { version = "0.34.6", features = ["compression", "no_metrics"] } sled = { version = "0.34.6", features = ["compression", "no_metrics"], optional = true }
rocksdb = { version = "0.16.0", features = ["multi-threaded-cf"] } rocksdb = { version = "0.16.0", features = ["multi-threaded-cf"], optional = true }
#sled = { git = "https://github.com/spacejam/sled.git", rev = "e4640e0773595229f398438886f19bca6f7326a2", features = ["compression"] } #sled = { git = "https://github.com/spacejam/sled.git", rev = "e4640e0773595229f398438886f19bca6f7326a2", features = ["compression"] }
# Used for the http request / response body type for Ruma endpoints used with reqwest # Used for the http request / response body type for Ruma endpoints used with reqwest
@ -75,7 +75,9 @@ opentelemetry-jaeger = "0.11.0"
pretty_env_logger = "0.4.0" pretty_env_logger = "0.4.0"
[features] [features]
default = ["conduit_bin"] default = ["conduit_bin", "backend_sled"]
backend_sled = ["sled"]
backend_rocksdb = ["rocksdb"]
conduit_bin = [] # TODO: add rocket to this when it is optional conduit_bin = [] # TODO: add rocket to this when it is optional
[[bin]] [[bin]]

View File

@ -621,7 +621,7 @@ async fn join_room_by_id_helper(
&pdu, &pdu,
utils::to_canonical_object(&pdu).expect("Pdu is valid canonical object"), utils::to_canonical_object(&pdu).expect("Pdu is valid canonical object"),
count, count,
pdu_id.into(), &pdu_id,
&[pdu.event_id.clone()], &[pdu.event_id.clone()],
db, db,
)?; )?;

View File

@ -77,8 +77,12 @@ fn default_log() -> String {
"info,state_res=warn,rocket=off,_=off,sled=off".to_owned() "info,state_res=warn,rocket=off,_=off,sled=off".to_owned()
} }
#[cfg(feature = "sled")]
pub type Engine = abstraction::SledEngine; pub type Engine = abstraction::SledEngine;
#[cfg(feature = "rocksdb")]
pub type Engine = abstraction::RocksDbEngine;
pub struct Database { pub struct Database {
pub globals: globals::Globals, pub globals: globals::Globals,
pub users: users::Users, pub users: users::Users,

View File

@ -1,21 +1,19 @@
use std::{
collections::BTreeMap,
future::Future,
pin::Pin,
sync::{Arc, RwLock},
};
use log::warn;
use rocksdb::{
BoundColumnFamily, ColumnFamilyDescriptor, DBWithThreadMode, Direction, MultiThreaded, Options,
};
use super::Config; use super::Config;
use crate::{utils, Result}; use crate::{utils, Result};
use log::warn;
use std::{future::Future, pin::Pin, sync::Arc};
#[cfg(feature = "rocksdb")]
use std::{collections::BTreeMap, sync::RwLock};
#[cfg(feature = "sled")]
pub struct SledEngine(sled::Db); pub struct SledEngine(sled::Db);
#[cfg(feature = "sled")]
pub struct SledEngineTree(sled::Tree); pub struct SledEngineTree(sled::Tree);
pub struct RocksDbEngine(rocksdb::DBWithThreadMode<MultiThreaded>);
#[cfg(feature = "rocksdb")]
pub struct RocksDbEngine(rocksdb::DBWithThreadMode<rocksdb::MultiThreaded>);
#[cfg(feature = "rocksdb")]
pub struct RocksDbEngineTree<'a> { pub struct RocksDbEngineTree<'a> {
db: Arc<RocksDbEngine>, db: Arc<RocksDbEngine>,
name: &'a str, name: &'a str,
@ -60,6 +58,7 @@ pub trait Tree: Send + Sync {
} }
} }
#[cfg(feature = "sled")]
impl DatabaseEngine for SledEngine { impl DatabaseEngine for SledEngine {
fn open(config: &Config) -> Result<Arc<Self>> { fn open(config: &Config) -> Result<Arc<Self>> {
Ok(Arc::new(SledEngine( Ok(Arc::new(SledEngine(
@ -76,6 +75,7 @@ impl DatabaseEngine for SledEngine {
} }
} }
#[cfg(feature = "sled")]
impl Tree for SledEngineTree { impl Tree for SledEngineTree {
fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>> { fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
Ok(self.0.get(key)?.map(|v| v.to_vec())) Ok(self.0.get(key)?.map(|v| v.to_vec()))
@ -165,29 +165,42 @@ impl Tree for SledEngineTree {
} }
} }
#[cfg(feature = "rocksdb")]
impl DatabaseEngine for RocksDbEngine { impl DatabaseEngine for RocksDbEngine {
fn open(config: &Config) -> Result<Arc<Self>> { fn open(config: &Config) -> Result<Arc<Self>> {
let mut db_opts = Options::default(); let mut db_opts = rocksdb::Options::default();
db_opts.create_if_missing(true); db_opts.create_if_missing(true);
db_opts.set_max_open_files(16);
db_opts.set_compaction_style(rocksdb::DBCompactionStyle::Level);
db_opts.set_compression_type(rocksdb::DBCompressionType::Snappy);
db_opts.set_target_file_size_base(256 << 20);
db_opts.set_write_buffer_size(256 << 20);
let cfs = DBWithThreadMode::<MultiThreaded>::list_cf(&db_opts, &config.database_path) let mut block_based_options = rocksdb::BlockBasedOptions::default();
.unwrap_or_default(); block_based_options.set_block_size(512 << 10);
db_opts.set_block_based_table_factory(&block_based_options);
let mut options = Options::default(); let cfs = rocksdb::DBWithThreadMode::<rocksdb::MultiThreaded>::list_cf(
&db_opts,
&config.database_path,
)
.unwrap_or_default();
let mut options = rocksdb::Options::default();
options.set_merge_operator_associative("increment", utils::increment_rocksdb); options.set_merge_operator_associative("increment", utils::increment_rocksdb);
let db = DBWithThreadMode::<MultiThreaded>::open_cf_descriptors( let db = rocksdb::DBWithThreadMode::<rocksdb::MultiThreaded>::open_cf_descriptors(
&db_opts, &db_opts,
&config.database_path, &config.database_path,
cfs.iter() cfs.iter()
.map(|name| ColumnFamilyDescriptor::new(name, options.clone())), .map(|name| rocksdb::ColumnFamilyDescriptor::new(name, options.clone())),
)?; )?;
Ok(Arc::new(RocksDbEngine(db))) Ok(Arc::new(RocksDbEngine(db)))
} }
fn open_tree(self: &Arc<Self>, name: &'static str) -> Result<Arc<dyn Tree>> { fn open_tree(self: &Arc<Self>, name: &'static str) -> Result<Arc<dyn Tree>> {
let mut options = Options::default(); let mut options = rocksdb::Options::default();
options.set_merge_operator_associative("increment", utils::increment_rocksdb); options.set_merge_operator_associative("increment", utils::increment_rocksdb);
// Create if it doesn't exist // Create if it doesn't exist
@ -201,12 +214,14 @@ impl DatabaseEngine for RocksDbEngine {
} }
} }
#[cfg(feature = "rocksdb")]
impl RocksDbEngineTree<'_> { impl RocksDbEngineTree<'_> {
fn cf(&self) -> BoundColumnFamily<'_> { fn cf(&self) -> rocksdb::BoundColumnFamily<'_> {
self.db.0.cf_handle(self.name).unwrap() self.db.0.cf_handle(self.name).unwrap()
} }
} }
#[cfg(feature = "rocksdb")]
impl Tree for RocksDbEngineTree<'_> { impl Tree for RocksDbEngineTree<'_> {
fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>> { fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
Ok(self.db.0.get_cf(self.cf(), key)?) Ok(self.db.0.get_cf(self.cf(), key)?)
@ -260,15 +275,20 @@ impl Tree for RocksDbEngineTree<'_> {
rocksdb::IteratorMode::From( rocksdb::IteratorMode::From(
from, from,
if backwards { if backwards {
Direction::Reverse rocksdb::Direction::Reverse
} else { } else {
Direction::Forward rocksdb::Direction::Forward
}, },
), ),
)) ))
} }
fn increment(&self, key: &[u8]) -> Result<Vec<u8>> { fn increment(&self, key: &[u8]) -> Result<Vec<u8>> {
let stats = rocksdb::perf::get_memory_usage_stats(Some(&[&self.db.0]), None).unwrap();
dbg!(stats.mem_table_total);
dbg!(stats.mem_table_unflushed);
dbg!(stats.mem_table_readers_total);
dbg!(stats.cache_total);
// TODO: atomic? // TODO: atomic?
let old = self.get(key)?; let old = self.get(key)?;
let new = utils::increment(old.as_deref()).unwrap(); let new = utils::increment(old.as_deref()).unwrap();
@ -285,7 +305,7 @@ impl Tree for RocksDbEngineTree<'_> {
.0 .0
.iterator_cf( .iterator_cf(
self.cf(), self.cf(),
rocksdb::IteratorMode::From(&prefix, Direction::Forward), rocksdb::IteratorMode::From(&prefix, rocksdb::Direction::Forward),
) )
.take_while(move |(k, _)| k.starts_with(&prefix)), .take_while(move |(k, _)| k.starts_with(&prefix)),
) )

View File

@ -19,8 +19,6 @@ use ruma::{
state_res::{self, Event, RoomVersion, StateMap}, state_res::{self, Event, RoomVersion, StateMap},
uint, EventId, RoomAliasId, RoomId, RoomVersionId, ServerName, UserId, uint, EventId, RoomAliasId, RoomId, RoomVersionId, ServerName, UserId,
}; };
use sled::IVec;
use std::{ use std::{
collections::{BTreeMap, HashMap, HashSet}, collections::{BTreeMap, HashMap, HashSet},
convert::{TryFrom, TryInto}, convert::{TryFrom, TryInto},
@ -34,7 +32,7 @@ use super::{abstraction::Tree, admin::AdminCommand, pusher};
/// ///
/// This is created when a state group is added to the database by /// This is created when a state group is added to the database by
/// hashing the entire state. /// hashing the entire state.
pub type StateHashId = IVec; pub type StateHashId = Vec<u8>;
pub struct Rooms { pub struct Rooms {
pub edus: edus::RoomEdus, pub edus: edus::RoomEdus,
@ -665,7 +663,7 @@ impl Rooms {
pdu: &PduEvent, pdu: &PduEvent,
mut pdu_json: CanonicalJsonObject, mut pdu_json: CanonicalJsonObject,
count: u64, count: u64,
pdu_id: IVec, pdu_id: &[u8],
leaves: &[EventId], leaves: &[EventId],
db: &Database, db: &Database,
) -> Result<()> { ) -> Result<()> {
@ -713,14 +711,13 @@ impl Rooms {
self.reset_notification_counts(&pdu.sender, &pdu.room_id)?; self.reset_notification_counts(&pdu.sender, &pdu.room_id)?;
self.pduid_pdu.insert( self.pduid_pdu.insert(
&pdu_id, pdu_id,
&serde_json::to_vec(&pdu_json).expect("CanonicalJsonObject is always a valid"), &serde_json::to_vec(&pdu_json).expect("CanonicalJsonObject is always a valid"),
)?; )?;
// This also replaces the eventid of any outliers with the correct // This also replaces the eventid of any outliers with the correct
// pduid, removing the place holder. // pduid, removing the place holder.
self.eventid_pduid self.eventid_pduid.insert(pdu.event_id.as_bytes(), pdu_id)?;
.insert(pdu.event_id.as_bytes(), &*pdu_id)?;
// See if the event matches any known pushers // See if the event matches any known pushers
for user in db for user in db
@ -1360,7 +1357,7 @@ impl Rooms {
&pdu, &pdu,
pdu_json, pdu_json,
count, count,
pdu_id.clone().into(), &pdu_id,
// Since this PDU references all pdu_leaves we can update the leaves // Since this PDU references all pdu_leaves we can update the leaves
// of the room // of the room
&[pdu.event_id.clone()], &[pdu.event_id.clone()],

View File

@ -91,8 +91,6 @@ enum TransactionStatus {
impl Sending { impl Sending {
pub fn start_handler(&self, db: Arc<Database>, mut receiver: mpsc::UnboundedReceiver<Vec<u8>>) { pub fn start_handler(&self, db: Arc<Database>, mut receiver: mpsc::UnboundedReceiver<Vec<u8>>) {
let db = db.clone();
tokio::spawn(async move { tokio::spawn(async move {
let mut futures = FuturesUnordered::new(); let mut futures = FuturesUnordered::new();

View File

@ -23,11 +23,13 @@ pub type Result<T> = std::result::Result<T, Error>;
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum Error { pub enum Error {
#[cfg(feature = "sled")]
#[error("There was a problem with the connection to the sled database.")] #[error("There was a problem with the connection to the sled database.")]
SledError { SledError {
#[from] #[from]
source: sled::Error, source: sled::Error,
}, },
#[cfg(feature = "rocksdb")]
#[error("There was a problem with the connection to the rocksdb database: {source}")] #[error("There was a problem with the connection to the rocksdb database: {source}")]
RocksDbError { RocksDbError {
#[from] #[from]

View File

@ -1,14 +1,15 @@
use crate::{Database, Error}; use crate::Error;
use ruma::{ use ruma::{
api::OutgoingResponse, api::OutgoingResponse,
identifiers::{DeviceId, UserId}, identifiers::{DeviceId, UserId},
Outgoing, signatures::CanonicalJsonValue,
Outgoing, ServerName,
}; };
use std::{ops::Deref, sync::Arc}; use std::ops::Deref;
#[cfg(feature = "conduit_bin")] #[cfg(feature = "conduit_bin")]
use { use {
crate::server_server, crate::{server_server, Database},
log::{debug, warn}, log::{debug, warn},
rocket::{ rocket::{
data::{self, ByteUnit, Data, FromData}, data::{self, ByteUnit, Data, FromData},
@ -18,14 +19,11 @@ use {
tokio::io::AsyncReadExt, tokio::io::AsyncReadExt,
Request, State, Request, State,
}, },
ruma::{ ruma::api::{AuthScheme, IncomingRequest},
api::{AuthScheme, IncomingRequest},
signatures::CanonicalJsonValue,
ServerName,
},
std::collections::BTreeMap, std::collections::BTreeMap,
std::convert::TryFrom, std::convert::TryFrom,
std::io::Cursor, std::io::Cursor,
std::sync::Arc,
}; };
/// This struct converts rocket requests into ruma structs by converting them into http requests /// This struct converts rocket requests into ruma structs by converting them into http requests

View File

@ -1681,7 +1681,7 @@ pub(crate) fn append_incoming_pdu(
pdu, pdu,
pdu_json, pdu_json,
count, count,
pdu_id.clone().into(), &pdu_id,
&new_room_leaves.into_iter().collect::<Vec<_>>(), &new_room_leaves.into_iter().collect::<Vec<_>>(),
&db, &db,
)?; )?;

View File

@ -1,7 +1,6 @@
use argon2::{Config, Variant}; use argon2::{Config, Variant};
use cmp::Ordering; use cmp::Ordering;
use rand::prelude::*; use rand::prelude::*;
use rocksdb::MergeOperands;
use ruma::serde::{try_from_json_map, CanonicalJsonError, CanonicalJsonObject}; use ruma::serde::{try_from_json_map, CanonicalJsonError, CanonicalJsonObject};
use std::{ use std::{
cmp, cmp,
@ -16,10 +15,11 @@ pub fn millis_since_unix_epoch() -> u64 {
.as_millis() as u64 .as_millis() as u64
} }
#[cfg(feature = "rocksdb")]
pub fn increment_rocksdb( pub fn increment_rocksdb(
_new_key: &[u8], _new_key: &[u8],
old: Option<&[u8]>, old: Option<&[u8]>,
_operands: &mut MergeOperands, _operands: &mut rocksdb::MergeOperands,
) -> Option<Vec<u8>> { ) -> Option<Vec<u8>> {
increment(old) increment(old)
} }