Support zfs page size

This commit is contained in:
William Brown 2020-08-04 16:52:57 +10:00 committed by Firstyear
parent 85ec82832e
commit c626eb60a6
11 changed files with 97 additions and 34 deletions

View file

@ -13,3 +13,6 @@
* Triss Healy (NiryaAestus)
* Samuel Cabrero (scabrero)
## Acknowledgements
* M. Gerstner

View file

@ -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"

View file

@ -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"

View file

@ -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<Self, OperationError> {
let db = IdlSqlite::new(audit, path, pool_size)?;
pub fn new(
audit: &mut AuditScope,
path: &str,
pool_size: u32,
fstype: FsType,
) -> Result<Self, OperationError> {
let db = IdlSqlite::new(audit, path, pool_size, fstype)?;
let entry_cache = Arc::new(
DEFAULT_CACHE_TARGET,
pool_size as usize,

View file

@ -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<Self, OperationError> {
let manager = SqliteConnectionManager::file(path);
pub fn new(
audit: &mut AuditScope,
path: &str,
pool_size: u32,
fstype: FsType,
) -> Result<Self, OperationError> {
// 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);

View file

@ -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<IdxKey>,
) -> Result<Self, OperationError> {
// 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();

View file

@ -20,6 +20,7 @@ pub struct Configuration {
pub threads: usize,
// db type later
pub db_path: String,
pub db_fs_type: Option<String>,
pub maximum_request: usize,
pub secure_cookies: bool,
pub tls_config: Option<TlsConfiguration>,
@ -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<String>) {
self.db_fs_type = p.as_ref().map(|v| v.to_lowercase());
}
pub fn update_bind(&mut self, b: &Option<String>) {
self.address = b
.as_ref()

View file

@ -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<Backend, Ope
let mut audit_be = AuditScope::new("backend_setup", uuid::Uuid::new_v4(), config.log_level);
let pool_size: u32 = config.threads as u32;
let be = Backend::new(&mut audit_be, config.db_path.as_str(), pool_size, idxmeta);
let fstype: FsType = if config
.db_fs_type
.as_ref()
.map(|s| s == "zfs")
.unwrap_or(false)
{
FsType::ZFS
} else {
FsType::Generic
};
let be = Backend::new(
&mut audit_be,
config.db_path.as_str(),
pool_size,
fstype,
idxmeta,
);
// debug!
audit_be.write_log();
be

View file

@ -2,7 +2,7 @@
macro_rules! run_test_no_init {
($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;
@ -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

View file

@ -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;

View file

@ -33,6 +33,7 @@ struct ServerConfig {
pub ldapbindaddress: Option<String>,
// pub threads: Option<usize>,
pub db_path: String,
pub db_fs_type: Option<String>,
pub tls_ca: Option<String>,
pub tls_cert: Option<String>,
pub tls_key: Option<String>,
@ -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);