[fractal/backend: 4/9] Thread local singleton for the connection



commit 379f2ffe9ec5481516283ee16e8c532a5a208f69
Author: Daniel García Moreno <danigm wadobo com>
Date:   Mon Dec 24 17:05:01 2018 +0100

    Thread local singleton for the connection
    
    This allow us to implement tests and run with multiple threads without
    problems because the connection will not be shared.

 Cargo.lock                      | 28 ++++++++++++++++++++---
 fractal-backend/Cargo.toml      |  2 +-
 fractal-backend/src/lib.rs      | 50 +++++++++++++++++++++++++++++++++++++++++
 fractal-backend/tests/models.rs | 17 +++++++-------
 4 files changed, 84 insertions(+), 13 deletions(-)
---
diff --git a/Cargo.lock b/Cargo.lock
index 99cf195b..d9743b21 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -465,11 +465,19 @@ name = "fractal-backend"
 version = "0.1.0"
 dependencies = [
  "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+<<<<<<< HEAD
  "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "fractal-matrix-api 4.0.0",
  "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "rusqlite 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)",
+=======
+ "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "fractal-matrix-api 4.0.0",
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rusqlite 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)",
+>>>>>>> Thread local singleton for the connection
 ]
 
 [[package]]
@@ -1158,7 +1166,7 @@ dependencies = [
 
 [[package]]
 name = "libsqlite3-sys"
-version = "0.10.0"
+version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 dependencies = [
  "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2020,13 +2028,17 @@ dependencies = [
 
 [[package]]
 name = "rusqlite"
-version = "0.15.0"
+version = "0.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 dependencies = [
  "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "libsqlite3-sys 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libsqlite3-sys 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "lru-cache 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+<<<<<<< HEAD
  "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
+=======
+ "time 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
+>>>>>>> Thread local singleton for the connection
 ]
 
 [[package]]
@@ -2767,9 +2779,14 @@ dependencies = [
 "checksum libc 0.2.47 (registry+https://github.com/rust-lang/crates.io-index)" = 
"48450664a984b25d5b479554c29cc04e3150c97aa4c01da5604a2d4ed9151476"
 "checksum libdbus-sys 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = 
"18cb88963258d00f4962205dbb5933d82780d9962c8c8a064b651d2ad7189210"
 "checksum libflate 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)" = 
"bff3ac7d6f23730d3b533c35ed75eef638167634476a499feef16c428d74b57b"
+<<<<<<< HEAD
 "checksum libhandy 0.1.0 (git+https://gitlab.gnome.org/jsparber/libhandy-rs)" = "<none>"
 "checksum libhandy-sys 0.1.0 (git+https://gitlab.gnome.org/jsparber/libhandy-rs)" = "<none>"
 "checksum libsqlite3-sys 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = 
"742b695cbfb89e549dca6960a55e6802f67d352e33e97859ee46dee835211b0f"
+=======
+"checksum libhandy 0.2.0 (git+https://gitlab.gnome.org/jsparber/libhandy-rs)" = "<none>"
+"checksum libsqlite3-sys 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = 
"6864266fa0d613f5382997f6152280edeccfd46173285459b627d6319526f5c9"
+>>>>>>> Thread local singleton for the connection
 "checksum linked-hash-map 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = 
"7860ec297f7008ff7a1e3382d7f7e1dcd69efc94751a2284bafc3d013c2aa939"
 "checksum linkify 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = 
"9ce9439c6f4a1092dc1861272bef01034891da39f13aa1cdcf40ca3e4081de5f"
 "checksum lmdb 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = 
"5b0908efb5d6496aa977d96f91413da2635a902e5e31dbef0bfb88986c248539"
@@ -2860,8 +2877,13 @@ dependencies = [
 "checksum regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = 
"7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7"
 "checksum regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = 
"4e47a2ed29da7a9e1960e1639e7a982e6edc6d49be308a3b02daf511504a16d1"
 "checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = 
"3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5"
+<<<<<<< HEAD
 "checksum reqwest 0.9.8 (registry+https://github.com/rust-lang/crates.io-index)" = 
"0e60f169af3915c294818d55dde549f00d2966cef36d6c5e7255d75df3f2b16f"
 "checksum rusqlite 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = 
"39bae767eb27866f5c0be918635ae54af705bc09db11be2c43a3c6b361cf3462"
+=======
+"checksum reqwest 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = 
"ab52e462d1e15891441aeefadff68bdea005174328ce3da0a314f2ad313ec837"
+"checksum rusqlite 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = 
"6381ddfe91dbb659b4b132168da15985bc84162378cf4fcdc4eb99c857d063e2"
+>>>>>>> Thread local singleton for the connection
 "checksum rust-crypto 0.2.36 
(git+https://github.com/awmath/rust-crypto?rev=394c247254dbe2ac5d44483232cf335d10cf0260)" = "<none>"
 "checksum rust-gmp 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = 
"4cd7d57377b309a73f69e164109203aa9ab3fee6ea68ac5fb76e2edb50662e9b"
 "checksum rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = 
"adacaae16d02b6ec37fdc7acfcddf365978de76d1983d3ee22afc260e1ca9619"
diff --git a/fractal-backend/Cargo.toml b/fractal-backend/Cargo.toml
index 9f5e8bc0..33236bee 100644
--- a/fractal-backend/Cargo.toml
+++ b/fractal-backend/Cargo.toml
@@ -4,7 +4,7 @@ version = "0.1.0"
 authors = ["Daniel García Moreno <danigm wadobo com>"]
 
 [dependencies]
-rusqlite = "0.15.0"
+rusqlite = "0.16.0"
 failure = "0.1.3"
 lazy_static = "1.2.0"
 serde_json = "1.0.33"
diff --git a/fractal-backend/src/lib.rs b/fractal-backend/src/lib.rs
index 41673c8e..097b76ea 100644
--- a/fractal-backend/src/lib.rs
+++ b/fractal-backend/src/lib.rs
@@ -1,3 +1,16 @@
+//! This crate defines a singleton for the database connection, the use of
+//! `init` or `init_local` will initialize the database and that will be used by
+//! all models queries to database
+//!
+//! If you want to use the connection as a process singleton, shared by all
+//! threads, use the `init` function. In other case use the `init_local` that
+//! provides a thread local singleton so each thread should call to `init_local`
+//! and will have an unique database connection.
+//!
+//! Don't merge the `init` and the `init_local` function calls in the same
+//! program. In any case, the local will take preference, so if you merge those
+//! two, the local will be used if it's initialized.
+
 extern crate chrono;
 extern crate failure;
 extern crate fractal_matrix_api as api;
@@ -9,6 +22,7 @@ extern crate lazy_static;
 
 pub mod model;
 
+use std::cell::RefCell;
 use std::sync::Arc;
 use std::sync::Mutex;
 
@@ -16,16 +30,36 @@ use failure::err_msg;
 use failure::Error;
 use rusqlite::Connection;
 
+// Thread local singleton
+thread_local! {
+    static CONN_LOCAL: RefCell<Option<Connection>> = RefCell::new(None);
+}
+
 // CONN is a singleton
 lazy_static! {
     static ref CONN: Arc<Mutex<Option<Connection>>> = Arc::new(Mutex::new(None));
 }
 
+/// Function to run a query to the database with a connection
+///
+/// This function receives a closure that will receive a ref to the connection.
+/// The `def` value is the return value used when there's no connection or
+/// the connection is not created
 pub fn conn<T, F>(f: F, def: T) -> T
 where
     T: Sized,
     F: Fn(&Connection) -> T + Sized,
 {
+    // first we try with the thread local
+    if let Some(output) = CONN_LOCAL.with(|c| match *c.borrow() {
+        Some(ref c) => Some(f(c)),
+        None => None,
+    }) {
+        return output;
+    }
+
+    // If the thread local is none or doesn't exists we check the global
+    // singleton
     if let Ok(guard) = CONN.lock() {
         return match guard.as_ref() {
             Some(c) => f(c),
@@ -36,6 +70,10 @@ where
     def
 }
 
+/// Initialized the connection database as a singleton, shared by all threads
+/// and used in the `conn` function. The `path` should be a correct string for a
+/// sqlite database:
+/// https://sqlite.org/c3ref/open.html#urifilenamesinsqlite3open
 pub fn init(path: &str) -> Result<(), Error> {
     if let Ok(mut guard) = CONN.lock() {
         let conn = Connection::open(path).map_err(|e| err_msg(e.to_string()))?;
@@ -44,3 +82,15 @@ pub fn init(path: &str) -> Result<(), Error> {
 
     Ok(())
 }
+
+/// Initialized the connection database as a local thread variable, and used in
+/// the `conn` function. The `path` should be a correct string for a sqlite
+/// database:
+/// https://sqlite.org/c3ref/open.html#urifilenamesinsqlite3open
+pub fn init_local(path: &str) -> Result<(), Error> {
+    CONN_LOCAL.with(|c| -> Result<(), Error> {
+        let conn: Connection = Connection::open(path).map_err(|e| err_msg(e.to_string()))?;
+        *c.borrow_mut() = Some(conn);
+        Ok(())
+    })
+}
diff --git a/fractal-backend/tests/models.rs b/fractal-backend/tests/models.rs
index 462eb2a3..faf8c5b4 100644
--- a/fractal-backend/tests/models.rs
+++ b/fractal-backend/tests/models.rs
@@ -1,19 +1,18 @@
 extern crate fractal_backend;
 
-use fractal_backend::init;
+use fractal_backend::init_local as init;
 use fractal_backend::model::Message;
 use fractal_backend::model::Model;
 use fractal_backend::model::Room;
-use std::fs::remove_file;
 
 #[test]
 fn room_model() {
-    let _ = remove_file("/tmp/db.sqlite3");
-    let _ = init("/tmp/db.sqlite3").unwrap();
+    let _ = init("").unwrap();
 
-    let mut r = Room::new("ROOM ID".to_string(), Some("ROOM NAME".to_string()));
     let created = Room::create_table();
     assert!(created.is_ok());
+
+    let mut r = Room::new("ROOM ID".to_string(), Some("ROOM NAME".to_string()));
     let stored = r.store();
     assert!(stored.is_ok());
 
@@ -41,13 +40,13 @@ fn room_model() {
 
 #[test]
 fn message_model() {
-    let _ = remove_file("/tmp/db.sqlite3");
-    let _ = init("/tmp/db.sqlite3").unwrap();
+    let _ = init("").unwrap();
 
-    let mut msg = Message::default();
-    msg.id = Some("MSGID".to_string());
     let created = Message::create_table();
     assert!(created.is_ok());
+
+    let mut msg = Message::default();
+    msg.id = Some("MSGID".to_string());
     let stored = msg.store();
     assert!(stored.is_ok());
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]