From 46c251655db7bb3d9aa814b1a5dde85336b0b9b1 Mon Sep 17 00:00:00 2001 From: metamuffin Date: Sat, 20 Jan 2024 00:50:20 +0100 Subject: replace sled with redb --- base/Cargo.toml | 7 +- base/src/database.rs | 202 ++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 181 insertions(+), 28 deletions(-) (limited to 'base') diff --git a/base/Cargo.toml b/base/Cargo.toml index ec5cbe7..36b93bc 100644 --- a/base/Cargo.toml +++ b/base/Cargo.toml @@ -14,6 +14,9 @@ base64 = "0.21.5" tokio = { workspace = true } anyhow = "1.0.75" bincode = "2.0.0-rc.3" -sled = "0.34.7" -typed-sled = "0.2.3" rand = "0.8.5" +redb = "1.5.0" +serde_json = "1.0.111" + +[features] +db_json = [] diff --git a/base/src/database.rs b/base/src/database.rs index 3f81cca..2a57937 100644 --- a/base/src/database.rs +++ b/base/src/database.rs @@ -3,43 +3,193 @@ which is licensed under the GNU Affero General Public License (version 3); see /COPYING. Copyright (C) 2023 metamuffin */ -use anyhow::Context; +use bincode::{Decode, Encode}; use jellycommon::{ user::{NodeUserData, User}, Node, }; use log::info; -use std::path::Path; -use typed_sled::Tree; +use std::{borrow::Borrow, ops::Deref, path::Path}; -pub use sled; -pub use typed_sled; +pub use redb::*; -pub struct Database { - pub db: sled::Db, +pub const T_USER: TableDefinition<&str, Ser> = TableDefinition::new("user"); +pub const T_USER_NODE: TableDefinition<(&str, &str), Ser> = + TableDefinition::new("user_node"); +pub const T_INVITE: TableDefinition<&str, Ser<()>> = TableDefinition::new("invite"); +pub const T_NODE: TableDefinition<&str, Ser> = TableDefinition::new("node"); +pub const T_NODE_IMPORT: TableDefinition<&str, Ser, Node)>>> = + TableDefinition::new("node_import"); - pub user: Tree, - pub user_node: Tree<(String, String), NodeUserData>, - pub invite: Tree, - pub node: Tree, - - pub node_import: Tree, Node)>>, +pub struct DataAcid { + pub inner: redb::Database, } -impl Database { +impl DataAcid { pub fn open(path: &Path) -> Result { - info!("opening database… (might take up to O(n) time)"); - let db = sled::open(path).context("opening database")?; - info!("creating trees"); - let r = Ok(Self { - user: Tree::open(&db, "user"), - invite: Tree::open(&db, "invite"), - node: Tree::open(&db, "node"), - user_node: Tree::open(&db, "user_node"), - node_import: Tree::open(&db, "node_import"), - db, - }); + info!("database"); + let db = redb::Database::create(path)?; + let r = Self { inner: db }; + + { + let txn = r.begin_write()?; + drop(txn.open_table(T_INVITE)?); + drop(txn.open_table(T_USER)?); + drop(txn.open_table(T_USER_NODE)?); + drop(txn.open_table(T_NODE)?); + drop(txn.open_table(T_NODE_IMPORT)?); + txn.commit()?; + } + info!("ready"); - r + Ok(r) + } +} + +impl Deref for DataAcid { + type Target = Database; + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +pub trait TableExt { + fn get(self, db: &DataAcid, key: KeyRef) -> anyhow::Result>; + fn insert(self, db: &DataAcid, key: KeyRef, value: Value) -> anyhow::Result<()>; + fn remove(self, db: &DataAcid, key: KeyRef) -> anyhow::Result>; +} +impl<'a, 'b, 'c, Key, Value, KeyRef> TableExt + for TableDefinition<'a, Key, Ser> +where + Key: Borrow<::SelfType<'b>> + redb::RedbKey, + Value: bincode::Encode + bincode::Decode + std::fmt::Debug, + KeyRef: Borrow<::SelfType<'c>>, +{ + fn get(self, db: &DataAcid, key: KeyRef) -> anyhow::Result> { + let txn = db.inner.begin_read()?; + let table = txn.open_table(self)?; + let user = table.get(key)?.map(|v| v.value().0); + drop(table); + Ok(user) + } + fn insert(self, db: &DataAcid, key: KeyRef, value: Value) -> anyhow::Result<()> { + let txn = db.inner.begin_write()?; + let mut table = txn.open_table(self)?; + table.insert(key, Ser(value))?; + drop(table); + txn.commit()?; + Ok(()) + } + fn remove(self, db: &DataAcid, key: KeyRef) -> anyhow::Result> { + let txn = db.inner.begin_write()?; + let mut table = txn.open_table(self)?; + let prev = table.remove(key)?.map(|v| v.value().0); + drop(table); + txn.commit()?; + Ok(prev) + } +} + +// pub trait TableIterExt< +// 'a, +// Key: redb::RedbKey + 'static, +// Value: redb::RedbValue + 'static, +// F: FnOnce(&redb::Range<'a, Key, Value>) -> anyhow::Result, +// T: 'static, +// > +// { +// fn iter(self, db: &'a DataAcid, f: F) -> anyhow::Result; +// } +// impl<'a, Key, Value, F, T> TableIterExt<'a, Key, Value, F, T> +// for TableDefinition<'static, Key, Value> +// where +// Key: redb::RedbKey, +// Value: redb::RedbValue, +// F: FnOnce(&redb::Range<'a, Key, Value>) -> anyhow::Result, +// T: 'static, +// { +// fn iter(self, db: &DataAcid, f: F) -> anyhow::Result { +// let txn = db.begin_read()?; +// let table = txn.open_table(self)?; +// let iter = table.iter()?; +// let ret = f(&iter)?; +// drop(iter); +// drop(table); +// drop(txn); +// Ok(ret) +// } +// } + +#[derive(Debug)] +#[cfg(not(feature = "db_json"))] +pub struct Ser(pub T); +#[cfg(not(feature = "db_json"))] +impl RedbValue for Ser { + type SelfType<'a> = Ser + where + Self: 'a; + type AsBytes<'a> = Vec + where + Self: 'a; + + fn fixed_width() -> Option { + None + } + + fn from_bytes<'a>(data: &'a [u8]) -> Self::SelfType<'a> + where + Self: 'a, + { + Ser(bincode::decode_from_slice(data, bincode::config::legacy()) + .unwrap() + .0) + } + + fn as_bytes<'a, 'b: 'a>(value: &'a Self::SelfType<'b>) -> Self::AsBytes<'a> + where + Self: 'a, + Self: 'b, + { + bincode::encode_to_vec(&value.0, bincode::config::legacy()).unwrap() + } + + fn type_name() -> redb::TypeName { + TypeName::new("bincode") + } +} + +#[derive(Debug)] +#[cfg(feature = "db_json")] +pub struct Ser(pub T); +#[cfg(feature = "db_json")] +impl Deserialize<'a> + std::fmt::Debug> RedbValue for Ser { + type SelfType<'a> = Ser + where + Self: 'a; + type AsBytes<'a> = Vec + where + Self: 'a; + + fn fixed_width() -> Option { + None + } + + fn from_bytes<'a>(data: &'a [u8]) -> Self::SelfType<'a> + where + Self: 'a, + { + Ser(serde_json::from_slice(data).unwrap()) + } + + fn as_bytes<'a, 'b: 'a>(value: &'a Self::SelfType<'b>) -> Self::AsBytes<'a> + where + Self: 'a, + Self: 'b, + { + serde_json::to_vec(&value.0).unwrap() + } + + fn type_name() -> redb::TypeName { + TypeName::new("json") } } -- cgit v1.2.3-70-g09d2