txn_db/lib.rs
1//! # txn-db
2//!
3//! A multi-version concurrency control (MVCC) transaction engine: the layer
4//! that turns a key-value store into a transactional database.
5//!
6//! Every write produces a new version tagged with a commit
7//! [`Timestamp`], so readers get a stable snapshot of the data without ever
8//! blocking writers, and writers detect conflicts at commit time instead of
9//! holding locks for the lifetime of a transaction. `txn-db` is deliberately a
10//! *layer*, not a store: the version store is the [`VersionStore`] trait, so it
11//! composes on top of an LSM tree, a B-tree, or any backend that can keep
12//! timestamped versions of a key. Snapshot isolation is the model.
13//!
14//! ## The common case
15//!
16//! Begin a transaction, read and write through it, commit. Conflicts surface as
17//! a typed, retryable error.
18//!
19//! ```
20//! use txn_db::Db;
21//!
22//! let db = Db::new();
23//!
24//! // Write two keys in one atomic transaction.
25//! let mut tx = db.begin();
26//! tx.put(b"user:1:name".to_vec(), b"ada".to_vec());
27//! tx.put(b"user:1:role".to_vec(), b"admin".to_vec());
28//! tx.commit()?;
29//!
30//! // A later transaction reads a consistent snapshot.
31//! let tx = db.begin();
32//! assert_eq!(tx.get(b"user:1:name")?.as_deref(), Some(&b"ada"[..]));
33//! # Ok::<(), txn_db::TxnError>(())
34//! ```
35//!
36//! ## Snapshot isolation
37//!
38//! A transaction reads the database as of the instant it began. Commits made by
39//! other transactions afterward are invisible to it, and its own buffered
40//! writes are visible only to itself until it commits. At commit, the engine
41//! applies *first-committer-wins*: if any key the transaction wrote was changed
42//! by another transaction that committed after this one's snapshot, the commit
43//! is rejected with a retryable [`TxnError::Conflict`]. That rule is what
44//! prevents lost updates.
45//!
46//! ```
47//! use txn_db::{Db, TxnError};
48//!
49//! let db = Db::new();
50//!
51//! // Two transactions start from the same snapshot and write the same key.
52//! let mut a = db.begin();
53//! let mut b = db.begin();
54//! a.put(b"counter".to_vec(), b"1".to_vec());
55//! b.put(b"counter".to_vec(), b"2".to_vec());
56//!
57//! a.commit()?; // the first committer wins
58//! let err = b.commit().unwrap_err(); // the second is told to retry
59//! assert!(err.is_retryable());
60//! # Ok::<(), TxnError>(())
61//! ```
62//!
63//! ## The three tiers
64//!
65//! - **Tier 1** is the whole common case: [`Db::new`], [`Db::begin`], and the
66//! [`Transaction`] methods. No builder, no generics to name.
67//! - **Tier 2** is configuration through a builder, arriving in a later phase.
68//! - **Tier 3** is the [`VersionStore`] trait, the seam for custom backends,
69//! reachable through [`Db::with_store`].
70//!
71//! ## Status
72//!
73//! This is the `0.2` foundation: the public surface, the MVCC core, snapshot
74//! isolation, and write-write conflict detection over an in-memory store.
75//! Serializable isolation, a durable commit log via `wal-db`, and version
76//! garbage collection follow in later phases. The shape of the Tier-1 API is
77//! settled and will not change before `1.0`.
78
79#![cfg_attr(docsrs, feature(doc_cfg))]
80#![deny(warnings)]
81#![deny(missing_docs)]
82#![deny(unused_must_use)]
83#![deny(unused_results)]
84#![deny(clippy::unwrap_used)]
85#![deny(clippy::expect_used)]
86#![deny(clippy::todo)]
87#![deny(clippy::unimplemented)]
88#![deny(clippy::print_stdout)]
89#![deny(clippy::print_stderr)]
90#![deny(clippy::dbg_macro)]
91#![deny(clippy::unreachable)]
92#![forbid(unsafe_code)]
93
94mod db;
95mod error;
96mod oracle;
97mod store;
98mod sync;
99mod timestamp;
100mod txn;
101
102pub use crate::db::Db;
103pub use crate::error::{Result, TxnError};
104pub use crate::store::{MemoryStore, VersionStore, WriteEntry};
105pub use crate::timestamp::Timestamp;
106pub use crate::txn::{Snapshot, Transaction};
107
108/// The crate's common imports in one `use`.
109///
110/// Pulls in the database handle, the transaction and snapshot types, the
111/// timestamp, and the error type — everything the Tier-1 common case touches.
112///
113/// # Examples
114///
115/// ```
116/// use txn_db::prelude::*;
117///
118/// let db = Db::new();
119/// let mut tx = db.begin();
120/// tx.put(b"k".to_vec(), b"v".to_vec());
121/// let _ts: Timestamp = tx.commit()?;
122/// # Ok::<(), TxnError>(())
123/// ```
124pub mod prelude {
125 pub use crate::db::Db;
126 pub use crate::error::{Result, TxnError};
127 pub use crate::store::{MemoryStore, VersionStore, WriteEntry};
128 pub use crate::timestamp::Timestamp;
129 pub use crate::txn::{Snapshot, Transaction};
130}