Browse Source

Add note about multithreaded/async programming and generally update docs.

Kestrel 1 year ago
parent
commit
31d39b4bce
4 changed files with 393 additions and 3 deletions
  1. 322 0
      Cargo.lock
  2. 60 1
      README.md
  3. 1 0
      microrm/Cargo.toml
  4. 10 2
      microrm/src/query.rs

+ 322 - 0
Cargo.lock

@@ -2,6 +2,113 @@
 # It is not intended for manual editing.
 version = 3
 
+[[package]]
+name = "async-channel"
+version = "1.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319"
+dependencies = [
+ "concurrent-queue",
+ "event-listener",
+ "futures-core",
+]
+
+[[package]]
+name = "async-executor"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965"
+dependencies = [
+ "async-task",
+ "concurrent-queue",
+ "fastrand",
+ "futures-lite",
+ "once_cell",
+ "slab",
+]
+
+[[package]]
+name = "async-global-executor"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5262ed948da60dd8956c6c5aca4d4163593dddb7b32d73267c93dab7b2e98940"
+dependencies = [
+ "async-channel",
+ "async-executor",
+ "async-io",
+ "async-lock",
+ "blocking",
+ "futures-lite",
+ "num_cpus",
+ "once_cell",
+]
+
+[[package]]
+name = "async-io"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5e18f61464ae81cde0a23e713ae8fd299580c54d697a35820cfd0625b8b0e07"
+dependencies = [
+ "concurrent-queue",
+ "futures-lite",
+ "libc",
+ "log",
+ "once_cell",
+ "parking",
+ "polling",
+ "slab",
+ "socket2",
+ "waker-fn",
+ "winapi",
+]
+
+[[package]]
+name = "async-lock"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6"
+dependencies = [
+ "event-listener",
+]
+
+[[package]]
+name = "async-std"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d"
+dependencies = [
+ "async-channel",
+ "async-global-executor",
+ "async-io",
+ "async-lock",
+ "crossbeam-utils",
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-lite",
+ "gloo-timers",
+ "kv-log-macro",
+ "log",
+ "memchr",
+ "once_cell",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+ "wasm-bindgen-futures",
+]
+
+[[package]]
+name = "async-task"
+version = "4.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524"
+
+[[package]]
+name = "atomic-waker"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a"
+
 [[package]]
 name = "atty"
 version = "0.2.14"
@@ -40,6 +147,20 @@ dependencies = [
  "generic-array",
 ]
 
+[[package]]
+name = "blocking"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc"
+dependencies = [
+ "async-channel",
+ "async-task",
+ "atomic-waker",
+ "fastrand",
+ "futures-lite",
+ "once_cell",
+]
+
 [[package]]
 name = "bstr"
 version = "0.2.17"
@@ -58,6 +179,12 @@ version = "3.9.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899"
 
+[[package]]
+name = "cache-padded"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c"
+
 [[package]]
 name = "cast"
 version = "0.2.7"
@@ -90,6 +217,15 @@ dependencies = [
  "unicode-width",
 ]
 
+[[package]]
+name = "concurrent-queue"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3"
+dependencies = [
+ "cache-padded",
+]
+
 [[package]]
 name = "convert_case"
 version = "0.5.0"
@@ -218,6 +354,16 @@ dependencies = [
  "memchr",
 ]
 
+[[package]]
+name = "ctor"
+version = "0.1.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c"
+dependencies = [
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "digest"
 version = "0.10.3"
@@ -234,6 +380,57 @@ version = "1.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
 
+[[package]]
+name = "event-listener"
+version = "2.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71"
+
+[[package]]
+name = "fastrand"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499"
+dependencies = [
+ "instant",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
+dependencies = [
+ "futures-core",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
+
+[[package]]
+name = "futures-io"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b"
+
+[[package]]
+name = "futures-lite"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48"
+dependencies = [
+ "fastrand",
+ "futures-core",
+ "futures-io",
+ "memchr",
+ "parking",
+ "pin-project-lite",
+ "waker-fn",
+]
+
 [[package]]
 name = "generic-array"
 version = "0.14.5"
@@ -255,6 +452,18 @@ dependencies = [
  "wasi",
 ]
 
+[[package]]
+name = "gloo-timers"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5fb7d06c1c8cc2a29bee7ec961009a0b2caa0793ee4900c2ffb348734ba1c8f9"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "js-sys",
+ "wasm-bindgen",
+]
+
 [[package]]
 name = "half"
 version = "1.8.2"
@@ -270,6 +479,15 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "instant"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
+dependencies = [
+ "cfg-if",
+]
+
 [[package]]
 name = "itertools"
 version = "0.10.3"
@@ -300,6 +518,15 @@ dependencies = [
  "wasm-bindgen",
 ]
 
+[[package]]
+name = "kv-log-macro"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f"
+dependencies = [
+ "log",
+]
+
 [[package]]
 name = "lazy_static"
 version = "1.4.0"
@@ -319,6 +546,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
 dependencies = [
  "cfg-if",
+ "value-bag",
 ]
 
 [[package]]
@@ -340,6 +568,7 @@ dependencies = [
 name = "microrm"
 version = "0.3.1"
 dependencies = [
+ "async-std",
  "base64",
  "criterion",
  "lazy_static",
@@ -383,12 +612,36 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "once_cell"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
+
 [[package]]
 name = "oorandom"
 version = "11.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
 
+[[package]]
+name = "parking"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72"
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
 [[package]]
 name = "pkg-config"
 version = "0.3.25"
@@ -423,6 +676,19 @@ dependencies = [
  "plotters-backend",
 ]
 
+[[package]]
+name = "polling"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "log",
+ "wepoll-ffi",
+ "winapi",
+]
+
 [[package]]
 name = "ppv-lite86"
 version = "0.2.16"
@@ -619,6 +885,25 @@ dependencies = [
  "digest",
 ]
 
+[[package]]
+name = "slab"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "socket2"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
 [[package]]
 name = "sqlite"
 version = "0.26.0"
@@ -703,12 +988,28 @@ version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
 
+[[package]]
+name = "value-bag"
+version = "1.0.0-alpha.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2209b78d1249f7e6f3293657c9779fe31ced465df091bbd433a1cf88e916ec55"
+dependencies = [
+ "ctor",
+ "version_check",
+]
+
 [[package]]
 name = "version_check"
 version = "0.9.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
 
+[[package]]
+name = "waker-fn"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"
+
 [[package]]
 name = "walkdir"
 version = "2.3.2"
@@ -751,6 +1052,18 @@ dependencies = [
  "wasm-bindgen-shared",
 ]
 
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f741de44b75e14c35df886aff5f1eb73aa114fa5d4d00dcd37b5e01259bf3b2"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
 [[package]]
 name = "wasm-bindgen-macro"
 version = "0.2.80"
@@ -790,6 +1103,15 @@ dependencies = [
  "wasm-bindgen",
 ]
 
+[[package]]
+name = "wepoll-ffi"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb"
+dependencies = [
+ "cc",
+]
+
 [[package]]
 name = "winapi"
 version = "0.3.9"

+ 60 - 1
README.md

@@ -11,7 +11,8 @@ supported though are possible. However, since by design microrm does not
 touch database contents for tables not defined in its model, using raw SQL
 for any needed dynamic components may be a better choice.
 
-Querying supports a small subset of SQL expressed as type composition.
+Querying supports a small subset of SQL expressed as type composition; see
+[`QueryInterface`](query/struct.QueryInterface.html) for more details.
 
 A simple example using an SQLite table as an (indexed) key/value store
 might look something like this:
@@ -57,3 +58,61 @@ The schema output from the loop is (details subject to change based on internals
 CREATE TABLE IF NOT EXISTS "kv_store" (id integer primary key,"key" text,"value" text);
 CREATE UNIQUE INDEX "kv_store_index" ON "kv_store" ("key");
 ```
+
+If you're using `microrm` in a threaded or async environment, you'll need to
+use a [`DBPool`](struct.DBPool.html). You can then write code like this:
+
+```rust
+# use microrm::prelude::*;
+# use microrm::{Entity,make_index};
+# #[derive(Debug,Entity,serde::Serialize,serde::Deserialize)]
+# pub struct KVStore {
+    # pub key: String,
+    # pub value: String
+# }
+
+async fn insert_a(dbp: &microrm::DBPool<'_>) {
+    let qi = dbp.query_interface();
+    qi.add(&KVStore {
+        key: "a_key".to_string(),
+        value: "a_value".to_string()
+    });
+}
+
+async fn insert_b(dbp: &microrm::DBPool<'_>) {
+    let qi = dbp.query_interface();
+    qi.add(&KVStore {
+        key: "b_key".to_string(),
+        value: "b_value".to_string()
+    });
+}
+
+# async_std::task::block_on(async { main().await });
+// running in your favourite async runtime
+async fn main() {
+    # let schema = microrm::Schema::new().entity::<KVStore>();
+    let db = microrm::DB::new_in_memory(schema).unwrap();
+    let dbp = microrm::DBPool::new(&db);
+
+    let a = insert_a(&dbp);
+    let b = insert_b(&dbp);
+
+    b.await;
+    a.await;
+
+    let qi = dbp.query_interface();
+
+    let qr = qi.get().by(KVStore::Key, "a_key").one().unwrap();
+    assert_eq!(qr.is_some(), true);
+    assert_eq!(qr.as_ref().unwrap().key, "a_key");
+    assert_eq!(qr.as_ref().unwrap().value, "a_value");
+
+    let qr = qi.get().by(KVStore::Key, "b_key").one().unwrap();
+    assert_eq!(qr.is_some(), true);
+    assert_eq!(qr.as_ref().unwrap().key, "b_key");
+    assert_eq!(qr.as_ref().unwrap().value, "b_value");
+}
+```
+
+Note that between acquiring a [`QueryInterface`] reference and dropping it, you
+must not `.await` anything; the compiler will (appropriately) complain.

+ 1 - 0
microrm/Cargo.toml

@@ -24,6 +24,7 @@ log = "0.4.17"
 criterion = "0.3"
 rand = "0.8.5"
 stats_alloc = "0.1.10"
+async-std = "1.11"
 
 [[bench]]
 name = "simple_in_memory"

+ 10 - 2
microrm/src/query.rs

@@ -73,7 +73,8 @@ type CacheIndex = u64;
 /// As the query interface provides some level of caching, try to strive for as much sharing as
 /// possible. Passing around `QueryInterface` references instead of `DB` references is a good way
 /// to achieve this. However, `QueryInterface` is explicitly `!Send`, so you may need to use
-/// something like a `DBPool`.
+/// something like a [`DBPool`](../struct.DBPool.html) to acquire instances locally if you're using
+/// threads or asynchronous programming.
 pub struct QueryInterface<'l> {
     db: &'l crate::DB,
 
@@ -84,6 +85,8 @@ pub struct QueryInterface<'l> {
 }
 
 impl<'l> QueryInterface<'l> {
+    /// Creates a QueryInterface. Generally you probably want to be using
+    /// `DBPool::query_interface()`.
     pub fn new(db: &'l crate::DB) -> Self {
         Self {
             db,
@@ -139,6 +142,7 @@ where {
 }
 
 impl<'l> QueryInterface<'l> {
+    /// Add an entity to its table in the database. Alias for `add`.
     pub fn insert<T: Entity + serde::Serialize>(
         &self,
         m: &T,
@@ -146,7 +150,7 @@ impl<'l> QueryInterface<'l> {
         self.add(m)
     }
 
-    /// Add an entity to its table
+    /// Add an entity to its table in the database.
     pub fn add<T: Entity + serde::Serialize>(
         &self,
         m: &T,
@@ -186,6 +190,7 @@ impl<'l> QueryInterface<'l> {
 
 // General query interface
 impl<'l> QueryInterface<'l> {
+    /// Get an entity from its table, aka perform a `SELECT` query.
     pub fn get<'a, 'b, T: Entity>(&'a self) -> select::Select<'b, 'l, T>
     where
         'a: 'b,
@@ -193,6 +198,7 @@ impl<'l> QueryInterface<'l> {
         select::Select::new(self)
     }
 
+    /// Update an entity in its table, aka perform an `UPDATE` query, by selecting columns.
     pub fn update<'a, 'b, T: Entity>(&'a self) -> update::Update<'b, 'l, T>
     where
         'a: 'b,
@@ -200,6 +206,7 @@ impl<'l> QueryInterface<'l> {
         update::Update::new(self)
     }
 
+    /// Delete an entity from its table, aka perform a DELETE query.
     pub fn delete<'a, 'b, T: Entity>(&'a self) -> delete::Delete<'b, 'l, T>
     where
         'a: 'b,
@@ -209,6 +216,7 @@ impl<'l> QueryInterface<'l> {
 }
 
 impl<'l> QueryInterface<'l> {
+    /// Update an entity in its table, aka perform an `UPDATE` query, for all columns.
     pub fn put<T: Entity>(&self, what: &WithID<T>) -> Result<(), Error> {
         self.update().to(what.as_ref()).by(T::id_column(), &what.id()).exec()
     }