diff --git a/Cargo.lock b/Cargo.lock index 28054a7..b331975 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -106,6 +106,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + [[package]] name = "argh" version = "0.1.13" @@ -1230,6 +1236,7 @@ dependencies = [ name = "owl" version = "0.1.0" dependencies = [ + "arc-swap", "argh", "chrono", "crossbeam", diff --git a/Cargo.toml b/Cargo.toml index 14e5575..8d63f77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,3 +25,4 @@ env_logger = "0.11.8" parking_lot = { version = "0.12.3", features = ["send_guard"] } crossbeam = { version = "0.8.4", features = ["crossbeam-channel"] } once_cell = "1.21.3" +arc-swap = "1.7.1" diff --git a/owl_macro/src/lib.rs b/owl_macro/src/lib.rs index f989319..d51e8e9 100644 --- a/owl_macro/src/lib.rs +++ b/owl_macro/src/lib.rs @@ -61,10 +61,10 @@ pub fn relation(args: TokenStream, input: TokenStream) -> TokenStream { let relation_name = struct_name.to_string().to_case(Case::Snake); let expanded = quote! { - #[derive(owl::Serialize, owl::Deserialize, Default)] + #[derive(owl::Serialize, owl::Deserialize, Default, Clone)] #input_struct - #[derive(owl::Deserialize, owl::Serialize, Debug)] + #[derive(owl::Deserialize, owl::Serialize, Debug, Clone)] pub struct #struct_ref_name { pub id: Id, pub inner: owl::db::relation::RelationReference @@ -166,7 +166,7 @@ pub fn model(_: TokenStream, input: TokenStream) -> TokenStream { let relation_name = struct_name.to_string().to_case(Case::Snake); let expanded = quote! { - #[derive(serde::Serialize, serde::Deserialize)] + #[derive(serde::Serialize, serde::Deserialize, Clone)] #input_struct impl owl::db::store::Saveable for #struct_name {} diff --git a/src/db/field.rs b/src/db/field.rs index e389bd3..d6e6833 100644 --- a/src/db/field.rs +++ b/src/db/field.rs @@ -4,7 +4,7 @@ use chrono::Utc; use serde::{Deserialize, Deserializer, Serialize}; // historic field -#[derive(Deserialize, Serialize, Default, Debug, PartialEq)] +#[derive(Deserialize, Serialize, Default, Debug, PartialEq, Clone)] pub struct Historic { pub values: Vec<(chrono::DateTime, T)>, } diff --git a/src/db/mod.rs b/src/db/mod.rs index 96801ee..53049c4 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -1,3 +1,4 @@ +use arc_swap::ArcSwap; use parking_lot::{RwLock, RwLockReadGuard}; use std::{ ops::{Deref, DerefMut}, @@ -170,7 +171,7 @@ impl Database { } /// Update every model in `entries` according to `u(_)` - pub fn update( + pub fn update( &self, entries: &mut [Model], u: F, @@ -259,7 +260,7 @@ impl Database { )? }; let model = Model { - inner: Arc::new(RwLock::new(m)), + inner: Arc::new(ArcSwap::new(Arc::new(m))), }; if self.cached { @@ -284,9 +285,10 @@ impl Database { } let data = data.read(); + let data = (*data).deref(); unsafe { - Store::save(&T::model_id(), data.deref(), self.named, &*self.storage); + Store::save(&T::model_id(), data, self.named, &*self.storage); } } @@ -296,7 +298,7 @@ impl Database { data: T, ) -> Model { let model = Model { - inner: Arc::new(RwLock::new(data)), + inner: Arc::new(ArcSwap::new(Arc::new(data))), }; self.save_model(&model); let id = model.full_id().to_string(); @@ -313,7 +315,7 @@ impl Database { // TODO : cache eviction based on ref counts pub struct Model { - inner: Arc>, + inner: Arc>, } impl Model { @@ -325,7 +327,7 @@ impl Model { impl Model { pub fn from_raw(raw: T) -> Model { Model { - inner: Arc::new(RwLock::new(raw)), + inner: Arc::new(ArcSwap::new(Arc::new(raw))), } } @@ -337,23 +339,23 @@ impl Model { self.read().reference() } - pub fn read(&self) -> RwLockReadGuard { - self.inner.read() + pub fn read(&self) -> arc_swap::Guard> { + self.inner.load() } } -impl Model { +impl Model { pub fn write(&mut self, db: &Database, u: F) { - let mut me = self.inner.write(); - u(me.deref_mut()); - drop(me); + let mut me = (**self.inner.load()).clone(); + u(&mut me); + self.inner.swap(Arc::new(me)); db.save_model(self); } /// Update the model inline without writing to `Database` pub fn write_raw_inline(&mut self, u: F) { - let mut me = self.inner.write(); - u(me.deref_mut()); + let mut me = (**self.inner.load()).clone(); + u(&mut me); drop(me); } } @@ -367,7 +369,7 @@ impl ModelObj for Model impl Clone for Model { fn clone(&self) -> Self { Self { - inner: Arc::clone(&self.inner), + inner: self.inner.clone(), } } } diff --git a/src/db/model/file.rs b/src/db/model/file.rs index 275e911..1a418b4 100644 --- a/src/db/model/file.rs +++ b/src/db/model/file.rs @@ -17,7 +17,7 @@ fn sha256(input: &[u8]) -> String { } /// A generic file -#[derive(Debug, Clone)] +#[derive(Debug)] #[model] pub struct File { id: Id, diff --git a/src/db/model/location.rs b/src/db/model/location.rs index b710374..b3856d1 100644 --- a/src/db/model/location.rs +++ b/src/db/model/location.rs @@ -6,7 +6,7 @@ pub use crate as owl; use crate::db::id::Id; /// Represents a geographical location with an ID, coordinates, and optional address -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, PartialEq)] #[model] pub struct Location { pub id: Id, diff --git a/src/db/model/mail_address.rs b/src/db/model/mail_address.rs index a5262b6..ca02004 100644 --- a/src/db/model/mail_address.rs +++ b/src/db/model/mail_address.rs @@ -5,7 +5,7 @@ pub use crate as owl; use crate::db::id::Id; /// Represents a mail address with an ID and the address itself -#[derive(Debug, Clone)] +#[derive(Debug)] #[model] pub struct MailAddress { /// Unique identifier for the mail address diff --git a/src/db/model/person.rs b/src/db/model/person.rs index 61d17bc..abbd0b2 100644 --- a/src/db/model/person.rs +++ b/src/db/model/person.rs @@ -32,7 +32,7 @@ pub struct Person { pub locations: Historic>>, } -#[derive(Deserialize, Serialize, Debug, PartialEq)] +#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)] pub enum Gender { Male, Female, diff --git a/src/db/model/phone_number.rs b/src/db/model/phone_number.rs index 1027827..47bae25 100644 --- a/src/db/model/phone_number.rs +++ b/src/db/model/phone_number.rs @@ -5,7 +5,7 @@ pub use crate as owl; use crate::db::id::Id; /// Represents a phone number with an ID, the phone number itself, and the country code. -#[derive(Debug, Clone)] +#[derive(Debug)] #[model] pub struct PhoneNumber { id: Id, diff --git a/src/db/relation.rs b/src/db/relation.rs index da1f14a..42fe050 100644 --- a/src/db/relation.rs +++ b/src/db/relation.rs @@ -437,7 +437,7 @@ impl< } pub trait RelationRef: - Identifiable + for<'a> Deserialize<'a> + Serialize + Send + Sync + 'static + Identifiable + for<'a> Deserialize<'a> + Serialize + Send + Sync + 'static + Clone { fn top(&self) -> IdRef; fn sub(&self) -> IdRef; @@ -445,7 +445,7 @@ pub trait RelationRef: fn update_meta(&mut self, weight: Option, meta: Option); } -#[derive(Deserialize, Serialize, Debug)] +#[derive(Deserialize, Serialize, Debug, Clone)] pub struct RelationReference { pub top: IdRef, pub sub: IdRef,