diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 83490731b..fc89694fe 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -13,3 +13,6 @@ * Triss Healy (NiryaAestus) * Samuel Cabrero (scabrero) +## Acknowledgements + +* M. Gerstner diff --git a/kanidm_book/src/installing_the_server.md b/kanidm_book/src/installing_the_server.md index c8716a92d..e43bfb308 100644 --- a/kanidm_book/src/installing_the_server.md +++ b/kanidm_book/src/installing_the_server.md @@ -27,6 +27,12 @@ You will also need a config file in `/data/server.toml`. It's contents should be # ldapbindaddress = "127.0.0.1:3636" # The path to the kanidm database. db_path = "/data/kanidm.db" + # If you have a known filesystem, kanidm can tune sqlite to match. Valid choices are: + # [zfs, other] + # If you are unsure about this, default to other + # zfs: + # * sets sqlite pagesize to 64k, you should set recordsize=64k on the zfs filesystem. + # db_fs_type = "zfs" # TLS ca, certificate and key in pem format. All three must be commented, or present # tls_ca = "/data/ca.pem" # tls_cert = "/data/cert.pem" diff --git a/kanidmd/server.toml b/kanidmd/server.toml index 4419f2b70..6d8cba108 100644 --- a/kanidmd/server.toml +++ b/kanidmd/server.toml @@ -1,6 +1,7 @@ bindaddress = "127.0.0.1:8443" ldapbindaddress = "127.0.0.1:3636" db_path = "/tmp/kanidm.db" +db_fs_type = "zfs" tls_ca = "../insecure/ca.pem" tls_cert = "../insecure/cert.pem" tls_key = "../insecure/key.pem" diff --git a/kanidmd/src/lib/be/idl_arc_sqlite.rs b/kanidmd/src/lib/be/idl_arc_sqlite.rs index 72abaab4a..97a2bb00a 100644 --- a/kanidmd/src/lib/be/idl_arc_sqlite.rs +++ b/kanidmd/src/lib/be/idl_arc_sqlite.rs @@ -1,6 +1,6 @@ use crate::audit::AuditScope; use crate::be::idl_sqlite::{ - IdlSqlite, IdlSqliteReadTransaction, IdlSqliteTransaction, IdlSqliteWriteTransaction, + FsType, IdlSqlite, IdlSqliteReadTransaction, IdlSqliteTransaction, IdlSqliteWriteTransaction, }; use crate::be::{IdRawEntry, IDL}; use crate::entry::{Entry, EntryCommitted, EntrySealed}; @@ -848,8 +848,13 @@ impl<'a> IdlArcSqliteWriteTransaction<'a> { } impl IdlArcSqlite { - pub fn new(audit: &mut AuditScope, path: &str, pool_size: u32) -> Result { - let db = IdlSqlite::new(audit, path, pool_size)?; + pub fn new( + audit: &mut AuditScope, + path: &str, + pool_size: u32, + fstype: FsType, + ) -> Result { + let db = IdlSqlite::new(audit, path, pool_size, fstype)?; let entry_cache = Arc::new( DEFAULT_CACHE_TARGET, pool_size as usize, diff --git a/kanidmd/src/lib/be/idl_sqlite.rs b/kanidmd/src/lib/be/idl_sqlite.rs index ed4ed4210..a9ffa5a30 100644 --- a/kanidmd/src/lib/be/idl_sqlite.rs +++ b/kanidmd/src/lib/be/idl_sqlite.rs @@ -6,6 +6,7 @@ use idlset::IDLBitRange; use kanidm_proto::v1::{ConsistencyError, OperationError}; use r2d2::Pool; use r2d2_sqlite::SqliteConnectionManager; +use rusqlite::OpenFlags; use rusqlite::OptionalExtension; use rusqlite::NO_PARAMS; use std::convert::{TryFrom, TryInto}; @@ -17,6 +18,13 @@ use uuid::Uuid; const DBV_ID2ENTRY: &str = "id2entry"; const DBV_INDEXV: &str = "indexv"; +#[repr(u32)] +#[derive(Debug, Copy, Clone)] +pub enum FsType { + Generic = 4096, + ZFS = 65536, +} + #[derive(Debug)] pub struct IdSqliteEntry { id: i64, @@ -1090,18 +1098,6 @@ impl IdlSqliteWriteTransaction { } pub fn setup(&self, audit: &mut AuditScope) -> Result<(), OperationError> { - // Enable WAL mode, which is just faster and better. - // - // We have to use stmt + prepare because execute can't handle - // the "wal" row on result when this works! - self.conn - .prepare("PRAGMA journal_mode=WAL;") - .and_then(|mut wal_stmt| wal_stmt.query(NO_PARAMS).map(|_| ())) - .map_err(|e| { - ladmin_error!(audit, "sqlite error {:?}", e); - OperationError::SQLiteError - })?; - // This stores versions of components. For example: // ---------------------- // | id | version | @@ -1236,8 +1232,27 @@ impl IdlSqliteWriteTransaction { } impl IdlSqlite { - pub fn new(audit: &mut AuditScope, path: &str, pool_size: u32) -> Result { - let manager = SqliteConnectionManager::file(path); + pub fn new( + audit: &mut AuditScope, + path: &str, + pool_size: u32, + fstype: FsType, + ) -> Result { + // If provided, set the page size to match the tuning we want. By default we use 4096. The VACUUM + // immediately after is so that on db create the page size takes effect. + // + // Enable WAL mode, which is just faster and better for our needs. + let mut flags = OpenFlags::default(); + // Open with multi thread flags and locking options. + flags.insert(OpenFlags::SQLITE_OPEN_NO_MUTEX); + let manager = SqliteConnectionManager::file(path) + .with_init(move |c| { + c.execute_batch( + format!("PRAGMA page_size={}; VACUUM; PRAGMA journal_mode=WAL;", fstype as u32) + .as_str(), + ) + }) + .with_flags(flags); let builder1 = Pool::builder(); let builder2 = if path == "" { // We are in a debug mode, with in memory. We MUST have only @@ -1278,12 +1293,12 @@ impl IdlSqlite { #[cfg(test)] mod tests { use crate::audit::AuditScope; - use crate::be::idl_sqlite::{IdlSqlite, IdlSqliteTransaction}; + use crate::be::idl_sqlite::{FsType, IdlSqlite, IdlSqliteTransaction}; #[test] fn test_idl_sqlite_verify() { let mut audit = AuditScope::new("run_test", uuid::Uuid::new_v4(), None); - let be = IdlSqlite::new(&mut audit, "", 1).unwrap(); + let be = IdlSqlite::new(&mut audit, "", 1, FsType::Generic).unwrap(); let be_w = be.write(); let r = be_w.verify(); assert!(r.len() == 0); diff --git a/kanidmd/src/lib/be/mod.rs b/kanidmd/src/lib/be/mod.rs index 33d9cb973..89ca463de 100644 --- a/kanidmd/src/lib/be/mod.rs +++ b/kanidmd/src/lib/be/mod.rs @@ -28,6 +28,9 @@ use crate::be::idl_arc_sqlite::{ IdlArcSqliteWriteTransaction, }; +// Re-export this +pub use crate::be::idl_sqlite::FsType; + const FILTER_SEARCH_TEST_THRESHOLD: usize = 8; const FILTER_EXISTS_TEST_THRESHOLD: usize = 0; @@ -1289,12 +1292,13 @@ impl Backend { audit: &mut AuditScope, path: &str, pool_size: u32, + fstype: FsType, idxmeta: Set, ) -> Result { // this has a ::memory() type, but will path == "" work? lperf_trace_segment!(audit, "be::new", || { let be = Backend { - idlayer: Arc::new(IdlArcSqlite::new(audit, path, pool_size)?), + idlayer: Arc::new(IdlArcSqlite::new(audit, path, pool_size, fstype)?), idxmeta: Arc::new(CowCell::new(idxmeta)), }; @@ -1365,7 +1369,9 @@ mod tests { use super::super::audit::AuditScope; use super::super::entry::{Entry, EntryInit, EntryNew}; use super::IdxKey; - use super::{Backend, BackendTransaction, BackendWriteTransaction, OperationError, IDL}; + use super::{ + Backend, BackendTransaction, BackendWriteTransaction, FsType, OperationError, IDL, + }; use crate::value::{IndexType, PartialValue, Value}; macro_rules! run_test { @@ -1411,7 +1417,8 @@ mod tests { itype: IndexType::EQUALITY, }); - let be = Backend::new(&mut audit, "", 1, idxmeta).expect("Failed to setup backend"); + let be = Backend::new(&mut audit, "", 1, FsType::Generic, idxmeta) + .expect("Failed to setup backend"); let mut be_txn = be.write(); diff --git a/kanidmd/src/lib/config.rs b/kanidmd/src/lib/config.rs index 2da449437..5bdc2a056 100644 --- a/kanidmd/src/lib/config.rs +++ b/kanidmd/src/lib/config.rs @@ -20,6 +20,7 @@ pub struct Configuration { pub threads: usize, // db type later pub db_path: String, + pub db_fs_type: Option, pub maximum_request: usize, pub secure_cookies: bool, pub tls_config: Option, @@ -61,6 +62,7 @@ impl Configuration { ldapaddress: None, threads: num_cpus::get(), db_path: String::from(""), + db_fs_type: None, maximum_request: 262_144, // 256k // log type // log path @@ -84,6 +86,10 @@ impl Configuration { self.db_path = p.to_string(); } + pub fn update_db_fs_type(&mut self, p: &Option) { + self.db_fs_type = p.as_ref().map(|v| v.to_lowercase()); + } + pub fn update_bind(&mut self, b: &Option) { self.address = b .as_ref() diff --git a/kanidmd/src/lib/core/mod.rs b/kanidmd/src/lib/core/mod.rs index d07268a68..2ced4b86a 100644 --- a/kanidmd/src/lib/core/mod.rs +++ b/kanidmd/src/lib/core/mod.rs @@ -33,7 +33,7 @@ use crate::actors::v1_write::{ }; use crate::async_log; use crate::audit::AuditScope; -use crate::be::{Backend, BackendTransaction}; +use crate::be::{Backend, BackendTransaction, FsType}; use crate::crypto::setup_tls; use crate::filter::{Filter, FilterInvalid}; use crate::idm::server::IdmServer; @@ -1270,7 +1270,24 @@ fn setup_backend(config: &Configuration, schema: &Schema) -> Result {{ use crate::audit::AuditScope; - use crate::be::Backend; + use crate::be::{Backend, FsType}; use crate::schema::Schema; use crate::server::QueryServer; use crate::utils::duration_from_epoch_now; @@ -22,7 +22,7 @@ macro_rules! run_test_no_init { let schema_txn = schema_outer.write(); schema_txn.reload_idxmeta() }; - let be = match Backend::new(&mut audit, "", 1, idxmeta) { + let be = match Backend::new(&mut audit, "", 1, FsType::Generic, idxmeta) { Ok(be) => be, Err(e) => { audit.write_log(); @@ -46,7 +46,7 @@ macro_rules! run_test_no_init { macro_rules! run_test { ($test_fn:expr) => {{ use crate::audit::AuditScope; - use crate::be::Backend; + use crate::be::{Backend, FsType}; use crate::schema::Schema; use crate::server::QueryServer; use crate::utils::duration_from_epoch_now; @@ -66,7 +66,7 @@ macro_rules! run_test { let schema_txn = schema_outer.write(); schema_txn.reload_idxmeta() }; - let be = match Backend::new(&mut audit, "", 1, idxmeta) { + let be = match Backend::new(&mut audit, "", 1, FsType::Generic, idxmeta) { Ok(be) => be, Err(e) => { audit.write_log(); @@ -116,7 +116,7 @@ macro_rules! entry_str_to_account { macro_rules! run_idm_test { ($test_fn:expr) => {{ use crate::audit::AuditScope; - use crate::be::Backend; + use crate::be::{Backend, FsType}; use crate::idm::server::IdmServer; use crate::schema::Schema; use crate::server::QueryServer; @@ -137,7 +137,8 @@ macro_rules! run_idm_test { let schema_txn = schema_outer.write(); schema_txn.reload_idxmeta() }; - let be = Backend::new(&mut audit, "", 1, idxmeta).expect("Failed to init be"); + let be = + Backend::new(&mut audit, "", 1, FsType::Generic, idxmeta).expect("Failed to init be"); let test_server = QueryServer::new(be, schema_outer); test_server diff --git a/kanidmd/src/lib/plugins/macros.rs b/kanidmd/src/lib/plugins/macros.rs index f711b9a01..6aee54b4d 100644 --- a/kanidmd/src/lib/plugins/macros.rs +++ b/kanidmd/src/lib/plugins/macros.rs @@ -19,7 +19,7 @@ macro_rules! setup_test { let schema_txn = schema_outer.write(); schema_txn.reload_idxmeta() }; - let be = Backend::new($au, "", 1, idxmeta).expect("Failed to init BE"); + let be = Backend::new($au, "", 1, FsType::Generic, idxmeta).expect("Failed to init BE"); let qs = QueryServer::new(be, schema_outer); qs.initialise_helper($au, duration_from_epoch_now()) @@ -48,7 +48,7 @@ macro_rules! run_create_test { $check:expr ) => {{ use crate::audit::AuditScope; - use crate::be::Backend; + use crate::be::{Backend, FsType}; use crate::event::CreateEvent; use crate::schema::Schema; use crate::server::QueryServer; @@ -103,7 +103,7 @@ macro_rules! run_modify_test { $check:expr ) => {{ use crate::audit::AuditScope; - use crate::be::Backend; + use crate::be::{Backend, FsType}; use crate::event::ModifyEvent; use crate::schema::Schema; use crate::server::QueryServer; @@ -165,7 +165,7 @@ macro_rules! run_delete_test { $check:expr ) => {{ use crate::audit::AuditScope; - use crate::be::Backend; + use crate::be::{Backend, FsType}; use crate::event::DeleteEvent; use crate::schema::Schema; use crate::server::QueryServer; diff --git a/kanidmd/src/server/main.rs b/kanidmd/src/server/main.rs index 23e6b4d87..db1562ca1 100644 --- a/kanidmd/src/server/main.rs +++ b/kanidmd/src/server/main.rs @@ -33,6 +33,7 @@ struct ServerConfig { pub ldapbindaddress: Option, // pub threads: Option, pub db_path: String, + pub db_fs_type: Option, pub tls_ca: Option, pub tls_cert: Option, pub tls_key: Option, @@ -273,6 +274,7 @@ async fn main() { config.update_log_level(ll); config.update_db_path(&sconfig.db_path.as_str()); + config.update_db_fs_type(&sconfig.db_fs_type); config.update_tls(&sconfig.tls_ca, &sconfig.tls_cert, &sconfig.tls_key); config.update_bind(&sconfig.bindaddress); config.update_ldapbind(&sconfig.ldapbindaddress);