Crates.io | someday |
lib.rs | someday |
version | 0.6.0 |
source | src |
created_at | 2023-03-27 14:18:49.201383 |
updated_at | 2024-02-01 21:48:55.708298 |
description | Lock-free MVCC primitive |
homepage | |
repository | https://github.com/hinto-janai/someday |
max_upload_size | |
id | 822204 |
size | 152,744 |
someday
someday
is a multi-version concurrency control primitive.
A lock-free, but more memory hungry alternative to Mutex<T>
or RwLock<T>
.
Reader
's are lock-free and most of the time wait-free.
The single Writer
is lock-free, but may clone data.
When the Writer
wants to push()
updates to Reader
's, it must:
The old data can be cheaply reclaimed and re-used by the Writer
if there are no Reader
's hanging onto old Commit
's.
If there are still Reader
's hanging onto old data, the Writer
will clone the data such that it can continue.
someday
is best in situations where:
Your data:
and if you have many readers who:
and a writer that:
Mutex
, RwLock
)Increased memory use: The Writer
keeps at least two copies of the backing data structure, and Reader
's can keep an infinite amount (as long as they continue to hold onto references)
Deterministic patches: The patches/functions applied to your data must be deterministic, since the Writer
may apply them twice
Slow writes: Writes are slower than they would be directly against the backing data structure
someday
's API is similar to git
and semantically does similar actions.
The Writer
:
add()
to add a Patch
(function) to their datacommit()
'ingpush()
those changes to the Reader
'sReader
'sThe Reader(s)
:
head()
to cheaply acquire the latest "head" Commit
Commit
objects forever (although at the peril of memory-usage)Writer
calls push()
This example shows the typical use case where the Writer
:
commit()
push()
and the Reader
:
Commit
of the current dataWriter
publishes with push()
The code:
use someday::{
Patch,
Writer,Reader,
Commit,CommitRef,
CommitInfo,PushInfo,
};
// Create Reader/Writer for the string "hello".
let (r, mut w) = someday::new("hello".to_string());
// The readers see the data.
let commit: CommitRef<String> = r.head();
assert_eq!(commit.data, "hello");
assert_eq!(commit.timestamp, 0);
// Writer writes some data, but does not commit.
w.add(Patch::Ptr(|w, _| w.push_str(" world")));
// Nothing committed, data still the same everywhere.
let data: &String = w.data();
assert_eq!(*data, "hello");
// Patches not yet committed:
assert_eq!(w.staged().len(), 1);
// Readers still see old data.
assert_eq!(r.head().data, "hello");
// Writer writes some more data.
w.add(Patch::Ptr(|w, _| w.push_str("!")));
// Readers still see old data.
assert_eq!(r.head().data, "hello");
// Writer commits their patches.
let commit_info: CommitInfo = w.commit();
// The 2 operation were committed locally
// (only the Writer sees them).
assert_eq!(commit_info.patches, 2);
// Readers still see old data.
assert_eq!(r.head().data, "hello");
// Writer finally reveals those
// changes by calling `push()`.
let push_info: PushInfo = w.push();
assert_eq!(push_info.commits, 1);
// Now readers see updates.
let commit: CommitRef<String> = r.head();
assert_eq!(commit.data, "hello world!");
// Each call to `.commit()` added 1 to the timestamp.
assert_eq!(commit.timestamp, 1);
These features are for (de)serialization.
You can directly (de)serialize your data T
from a:
Writer<T>
Reader<T>
Commit<T>
Feature Flag | Purpose |
---|---|
serde |
Enables serde 's Serialize & Deserialize |
bincode |
Enables bincode 2.0.0-rc.3 's Encode & Decode |
borsh |
Enables borsh 's BorshSerialize & BorshDeserialize |
The Minimum Supported Rust Version is 1.70.0
.