aboutsummaryrefslogtreecommitdiff
path: root/base
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2024-01-20 00:50:20 +0100
committermetamuffin <metamuffin@disroot.org>2024-01-20 00:50:20 +0100
commit46c251655db7bb3d9aa814b1a5dde85336b0b9b1 (patch)
treeab0696f2c92e8854ce6aa0737877cc15184bd8b6 /base
parent1c37d32a0985ff7390313833345b9299f9f0b196 (diff)
downloadjellything-46c251655db7bb3d9aa814b1a5dde85336b0b9b1.tar
jellything-46c251655db7bb3d9aa814b1a5dde85336b0b9b1.tar.bz2
jellything-46c251655db7bb3d9aa814b1a5dde85336b0b9b1.tar.zst
replace sled with redb
Diffstat (limited to 'base')
-rw-r--r--base/Cargo.toml7
-rw-r--r--base/src/database.rs202
2 files changed, 181 insertions, 28 deletions
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 <metamuffin.org>
*/
-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<User>> = TableDefinition::new("user");
+pub const T_USER_NODE: TableDefinition<(&str, &str), Ser<NodeUserData>> =
+ TableDefinition::new("user_node");
+pub const T_INVITE: TableDefinition<&str, Ser<()>> = TableDefinition::new("invite");
+pub const T_NODE: TableDefinition<&str, Ser<Node>> = TableDefinition::new("node");
+pub const T_NODE_IMPORT: TableDefinition<&str, Ser<Vec<(Vec<usize>, Node)>>> =
+ TableDefinition::new("node_import");
- pub user: Tree<String, User>,
- pub user_node: Tree<(String, String), NodeUserData>,
- pub invite: Tree<String, ()>,
- pub node: Tree<String, Node>,
-
- pub node_import: Tree<String, Vec<(Vec<usize>, Node)>>,
+pub struct DataAcid {
+ pub inner: redb::Database,
}
-impl Database {
+impl DataAcid {
pub fn open(path: &Path) -> Result<Self, anyhow::Error> {
- 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<Key, KeyRef, Value> {
+ fn get(self, db: &DataAcid, key: KeyRef) -> anyhow::Result<Option<Value>>;
+ fn insert(self, db: &DataAcid, key: KeyRef, value: Value) -> anyhow::Result<()>;
+ fn remove(self, db: &DataAcid, key: KeyRef) -> anyhow::Result<Option<Value>>;
+}
+impl<'a, 'b, 'c, Key, Value, KeyRef> TableExt<Key, KeyRef, Value>
+ for TableDefinition<'a, Key, Ser<Value>>
+where
+ Key: Borrow<<Key as RedbValue>::SelfType<'b>> + redb::RedbKey,
+ Value: bincode::Encode + bincode::Decode + std::fmt::Debug,
+ KeyRef: Borrow<<Key as redb::RedbValue>::SelfType<'c>>,
+{
+ fn get(self, db: &DataAcid, key: KeyRef) -> anyhow::Result<Option<Value>> {
+ 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<Option<Value>> {
+ 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>,
+// T: 'static,
+// >
+// {
+// fn iter(self, db: &'a DataAcid, f: F) -> anyhow::Result<T>;
+// }
+// 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>,
+// T: 'static,
+// {
+// fn iter(self, db: &DataAcid, f: F) -> anyhow::Result<T> {
+// 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<T>(pub T);
+#[cfg(not(feature = "db_json"))]
+impl<T: Encode + Decode + std::fmt::Debug> RedbValue for Ser<T> {
+ type SelfType<'a> = Ser<T>
+ where
+ Self: 'a;
+ type AsBytes<'a> = Vec<u8>
+ where
+ Self: 'a;
+
+ fn fixed_width() -> Option<usize> {
+ 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<T>(pub T);
+#[cfg(feature = "db_json")]
+impl<T: Serialize + for<'a> Deserialize<'a> + std::fmt::Debug> RedbValue for Ser<T> {
+ type SelfType<'a> = Ser<T>
+ where
+ Self: 'a;
+ type AsBytes<'a> = Vec<u8>
+ where
+ Self: 'a;
+
+ fn fixed_width() -> Option<usize> {
+ 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")
}
}