Make rocksdb actually work
(also adds some poc social media functions for testing)
This commit is contained in:
parent
7c589922e6
commit
a8db282cf2
9 changed files with 143 additions and 23 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
|||
/target
|
||||
/.state
|
||||
|
|
8
Cargo.lock
generated
8
Cargo.lock
generated
|
@ -777,9 +777,8 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "librocksdb-sys"
|
||||
version = "0.16.0+8.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce3d60bc059831dc1c83903fb45c103f75db65c5a7bf22272764d9cc683e348c"
|
||||
version = "0.17.0+9.0.0"
|
||||
source = "git+https://github.com/rust-rocksdb/rust-rocksdb.git#961abc8e45b30b43cad3659305d5703eb349fc31"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"bzip2-sys",
|
||||
|
@ -1196,8 +1195,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "rocksdb"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6bd13e55d6d7b8cd0ea569161127567cd587676c99f4472f779a0279aa60a7a7"
|
||||
source = "git+https://github.com/rust-rocksdb/rust-rocksdb.git#961abc8e45b30b43cad3659305d5703eb349fc31"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"librocksdb-sys",
|
||||
|
|
|
@ -1,3 +1,13 @@
|
|||
use puppy::{store::alias::Username, Store};
|
||||
|
||||
fn main() {
|
||||
println!("pupctl")
|
||||
let db = Store::open(".state").unwrap();
|
||||
// let riley = puppy::create_author(&db, "riley").unwrap();
|
||||
let riley = db
|
||||
.transaction(|tx| tx.lookup_alias(Username("riley".to_string())))
|
||||
.unwrap();
|
||||
puppy::create_post(&db, riley, "hello!").unwrap();
|
||||
for (key, post) in puppy::list_posts_by_author(&db, riley).unwrap() {
|
||||
println!("post {key}: {:?} by user {riley}", post.content)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
|
||||
shellHook = ''
|
||||
export LIBCLANG_PATH="${pkgs.llvmPackages_16.libclang.lib}/lib";
|
||||
export ROCKSDB_LIB_DIR="${pkgs.rocksdb}/lib";
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1 +1,52 @@
|
|||
pub use store::{self, Key, Store};
|
||||
use store::{
|
||||
alias::Username,
|
||||
arrow::{AuthorOf, Direction},
|
||||
value::{Content, Profile},
|
||||
Keylike,
|
||||
};
|
||||
|
||||
pub fn create_post(db: &Store, author: Key, content: impl ToString) -> store::Result<Key> {
|
||||
let key = Key::gen();
|
||||
db.transaction(|tx| {
|
||||
tx.update::<Profile>(author, |_, mut profile| {
|
||||
profile.post_count += 1;
|
||||
Ok(profile)
|
||||
})?;
|
||||
tx.insert(key, Content {
|
||||
content: Some(content.to_string()),
|
||||
summary: None,
|
||||
})?;
|
||||
tx.insert_arrow((author, key), AuthorOf)?;
|
||||
Ok(key)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn create_author(db: &Store, username: impl ToString) -> store::Result<Key> {
|
||||
let key = Key::gen();
|
||||
db.transaction(|tx| {
|
||||
tx.insert_alias(key, Username(username.to_string()))?;
|
||||
tx.insert(key, Profile {
|
||||
post_count: 0,
|
||||
account_name: username.to_string(),
|
||||
display_name: None,
|
||||
about_string: None,
|
||||
about_fields: Vec::new(),
|
||||
})?;
|
||||
Ok(key)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn list_posts_by_author(
|
||||
db: &Store,
|
||||
author: impl Keylike,
|
||||
) -> store::Result<Vec<(Key, Content)>> {
|
||||
db.transaction(|tx| {
|
||||
tx.list_arrows_with::<AuthorOf>(Direction::Outgoing, author)
|
||||
.map(|r| {
|
||||
let (post_key, _) = r?;
|
||||
tx.lookup::<Content>(post_key)
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
|
|
@ -7,6 +7,6 @@ path = "src/lib.rs"
|
|||
|
||||
[dependencies]
|
||||
ulid = "*"
|
||||
rocksdb = "*"
|
||||
rocksdb = { git = "https://github.com/rust-rocksdb/rust-rocksdb.git" }
|
||||
derive_more = "*"
|
||||
bincode = "2.0.0-rc.3"
|
||||
|
|
|
@ -1,10 +1,21 @@
|
|||
use std::fmt::Display;
|
||||
|
||||
use crate::{Alias, Result, Transaction};
|
||||
|
||||
/// A unique identifier for vertices in the database.
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub struct Key([u8; 16]);
|
||||
|
||||
impl Display for Key {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
ulid::Ulid::from_bytes(self.0).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Key {
|
||||
pub fn gen() -> Key {
|
||||
Key(ulid::Ulid::new().to_bytes())
|
||||
}
|
||||
pub(crate) fn from_slice(buf: &[u8]) -> Key {
|
||||
let mut key = [0; 16];
|
||||
key.copy_from_slice(&buf);
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use derive_more::From;
|
||||
use rocksdb::MultiThreaded;
|
||||
use rocksdb::{MultiThreaded, Options, TransactionDBOptions};
|
||||
|
||||
type Backend = rocksdb::TransactionDB<MultiThreaded>;
|
||||
|
||||
|
@ -23,6 +23,21 @@ pub use key::{Key, Keylike};
|
|||
pub use transaction::Transaction;
|
||||
pub use {alias::Alias, arrow::Arrow, value::Value};
|
||||
|
||||
pub const OK: Result<()> = Ok(());
|
||||
|
||||
/// Master list of all column family names in use.
|
||||
const SPACES: &[&'static str] = &[
|
||||
"registry",
|
||||
"username/l",
|
||||
"username/r",
|
||||
"follows/l",
|
||||
"follows/r",
|
||||
"profile",
|
||||
"content",
|
||||
"created-by/l",
|
||||
"created-by/r",
|
||||
];
|
||||
|
||||
/// The handle to the data store.
|
||||
///
|
||||
/// This type can be cloned freely.
|
||||
|
@ -31,6 +46,23 @@ pub struct Store {
|
|||
inner: Arc<Backend>,
|
||||
}
|
||||
|
||||
impl Store {
|
||||
pub fn open(state_dir: &str) -> Result<Store> {
|
||||
let mut db_opts = Options::default();
|
||||
db_opts.create_if_missing(true);
|
||||
db_opts.create_missing_column_families(true);
|
||||
let tx_opts = TransactionDBOptions::default();
|
||||
// NOTE: it crashes here because there hasn't been a release yet that includes https://github.com/rust-rocksdb/rust-rocksdb/pull/868
|
||||
let inner = Arc::new(Backend::open_cf(
|
||||
&db_opts,
|
||||
&tx_opts,
|
||||
format!("{state_dir}/main-store"),
|
||||
SPACES,
|
||||
)?);
|
||||
Ok(Store { inner })
|
||||
}
|
||||
}
|
||||
|
||||
/// An isolated keyspace.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Space(&'static str);
|
||||
|
@ -54,11 +86,27 @@ pub mod value {
|
|||
}
|
||||
|
||||
#[derive(Encode, Decode)]
|
||||
pub struct Profile {}
|
||||
pub struct Profile {
|
||||
pub post_count: usize,
|
||||
pub account_name: String,
|
||||
pub display_name: Option<String>,
|
||||
pub about_string: Option<String>,
|
||||
pub about_fields: Vec<(String, String)>,
|
||||
}
|
||||
|
||||
impl Value for Profile {
|
||||
const SPACE: Space = Space("profile");
|
||||
}
|
||||
|
||||
#[derive(Encode, Decode)]
|
||||
pub struct Content {
|
||||
pub content: Option<String>,
|
||||
pub summary: Option<String>,
|
||||
}
|
||||
|
||||
impl Value for Content {
|
||||
const SPACE: Space = Space("content");
|
||||
}
|
||||
}
|
||||
|
||||
pub mod arrow {
|
||||
|
@ -78,6 +126,13 @@ pub mod arrow {
|
|||
Incoming,
|
||||
Outgoing,
|
||||
}
|
||||
|
||||
#[derive(Encode, Decode)]
|
||||
pub struct AuthorOf;
|
||||
|
||||
impl Arrow for AuthorOf {
|
||||
const SPACE: (Space, Space) = (Space("created-by/l"), Space("created-by/r"));
|
||||
}
|
||||
}
|
||||
|
||||
pub mod alias {
|
||||
|
|
|
@ -3,18 +3,9 @@ use std::{collections::HashMap, sync::Arc};
|
|||
use bincode::{Decode, Encode};
|
||||
use rocksdb::BoundColumnFamily;
|
||||
|
||||
use crate::{arrow::Direction, Alias, Arrow, Backend, Error, Key, Keylike, Result, Store, Value};
|
||||
|
||||
const OK: Result<()> = Ok(());
|
||||
/// Master list of all column family names in use.
|
||||
const SPACES: &[&'static str] = &[
|
||||
"registry",
|
||||
"username/l",
|
||||
"username/r",
|
||||
"follows/l",
|
||||
"follows/r",
|
||||
"profile",
|
||||
];
|
||||
use crate::{
|
||||
arrow::Direction, Alias, Arrow, Backend, Error, Key, Keylike, Result, Store, Value, OK, SPACES,
|
||||
};
|
||||
|
||||
impl Store {
|
||||
/// Initiate a transaction.
|
||||
|
@ -120,17 +111,21 @@ impl Transaction<'_> {
|
|||
Ok(Key::from_slice(raw.as_ref()))
|
||||
}
|
||||
/// Create a new alias of type `A` for the given [`Key`].
|
||||
///
|
||||
/// If the alias already exists, this function returns `Conflict`.
|
||||
pub fn insert_alias<A>(&self, key: Key, alias: A) -> Result<()>
|
||||
where
|
||||
A: Alias,
|
||||
{
|
||||
let (l, r) = A::SPACE;
|
||||
let alias = alias.to_string();
|
||||
if self.with(l).has(&alias)? {
|
||||
return Err(Error::Conflict);
|
||||
}
|
||||
self.with(l).set(&alias, key)?;
|
||||
self.with(r).set(key, &alias)?;
|
||||
OK
|
||||
}
|
||||
|
||||
/// Delete the alias of type `A` that points to `key`.
|
||||
pub fn remove_alias<A>(&self, key: Key) -> Result<()>
|
||||
where
|
||||
|
|
Loading…
Reference in a new issue