From dd4cb7fcc6eab2d23632a63fdafeb943aad354ac Mon Sep 17 00:00:00 2001
From: JMARyA <jmarya@hydrar.de>
Date: Sun, 4 May 2025 23:52:55 +0200
Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20no=20rwlocks?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 Cargo.lock                   |  7 +++++++
 Cargo.toml                   |  1 +
 owl_macro/src/lib.rs         |  6 +++---
 src/db/field.rs              |  2 +-
 src/db/mod.rs                | 32 +++++++++++++++++---------------
 src/db/model/file.rs         |  2 +-
 src/db/model/location.rs     |  2 +-
 src/db/model/mail_address.rs |  2 +-
 src/db/model/person.rs       |  2 +-
 src/db/model/phone_number.rs |  2 +-
 src/db/relation.rs           |  4 ++--
 11 files changed, 36 insertions(+), 26 deletions(-)

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<T> {
     pub values: Vec<(chrono::DateTime<Utc>, 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<T: 'static + Serialize + Identifiable + Send + Sync, F: Fn(&mut T)>(
+    pub fn update<T: 'static + Serialize + Identifiable + Send + Sync + Clone, F: Fn(&mut T)>(
         &self,
         entries: &mut [Model<T>],
         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<T> {
         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<T> {
-    inner: Arc<RwLock<T>>,
+    inner: Arc<ArcSwap<T>>,
 }
 
 impl Model<File> {
@@ -325,7 +327,7 @@ impl Model<File> {
 impl<T: Identifiable> Model<T> {
     pub fn from_raw(raw: T) -> Model<T> {
         Model {
-            inner: Arc::new(RwLock::new(raw)),
+            inner: Arc::new(ArcSwap::new(Arc::new(raw))),
         }
     }
 
@@ -337,23 +339,23 @@ impl<T: Identifiable> Model<T> {
         self.read().reference()
     }
 
-    pub fn read(&self) -> RwLockReadGuard<T> {
-        self.inner.read()
+    pub fn read(&self) -> arc_swap::Guard<Arc<T>> {
+        self.inner.load()
     }
 }
 
-impl<T: 'static + Serialize + Identifiable + Send + Sync> Model<T> {
+impl<T: 'static + Serialize + Identifiable + Send + Sync + Clone> Model<T> {
     pub fn write<F: Fn(&mut T)>(&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<F: Fn(&mut T)>(&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<T: 'static + Serialize + Identifiable + Send + Sync> ModelObj for Model<T>
 impl<T> Clone for Model<T> {
     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<Vec<IdRef<Location>>>,
 }
 
-#[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<serde_json::Value>;
     fn sub(&self) -> IdRef<serde_json::Value>;
@@ -445,7 +445,7 @@ pub trait RelationRef:
     fn update_meta(&mut self, weight: Option<f64>, meta: Option<serde_json::Value>);
 }
 
-#[derive(Deserialize, Serialize, Debug)]
+#[derive(Deserialize, Serialize, Debug, Clone)]
 pub struct RelationReference {
     pub top: IdRef<serde_json::Value>,
     pub sub: IdRef<serde_json::Value>,