Expand description
§lock-db
Lock manager and deadlock detection for Rust databases — row/range locks, multiple granularities, and wait-for cycle detection.
A lock manager is the component that lets many transactions touch shared
data at once without corrupting it. Each transaction asks for a lock on a
resource in a LockMode; the manager grants it only when the mode is
compatible with what every other transaction already holds. That single
rule — the compatibility matrix — is what keeps concurrent reads and writes
correct.
§What is in this release
This is the v0.3.0 milestone. It provides multi-granularity and range locking on top of the lock-table core:
LockMode— the five standard MGL modes (IS, IX, S, SIX, X) and their compatibility matrix, plus the lattice join that drives upgrades.LockManager— a sharded, non-blocking lock table with acquire, release, bulk release, lattice upgrades, and range locks.KeyRange— an inclusive key interval, the unit a range lock protects (phantom / predicate protection).TxnIdandResourceId— opaque identifiers the caller assigns.LockError— the small, exhaustive set of ways an operation can fail.
Acquisition is non-blocking: a request that cannot be granted returns
LockError::Conflict instead of waiting. Blocking acquisition with wait
queues and wait-for deadlock detection land in later 0.x releases (see
dev/ROADMAP.md).
§Hierarchical locking
The intention modes exist to lock a hierarchy — database, table, page, row —
correctly and cheaply. The protocol is: before locking a resource in S or
X, hold an intention lock on each coarser resource above it (IS above an
S, IX above an X), acquiring coarse-to-fine and releasing fine-to-
coarse. lock-db enforces the compatibility matrix at each level; the caller
follows the protocol and maps each hierarchy node to a ResourceId.
§Example
use lock_db::prelude::*;
let lm = LockManager::new();
let row = ResourceId::new(1);
let (writer, reader) = (TxnId::new(1), TxnId::new(2));
// The writer takes the row exclusively.
lm.try_acquire(writer, row, LockMode::Exclusive).unwrap();
// A concurrent reader is refused while the write lock is held.
assert_eq!(lm.try_acquire(reader, row, LockMode::Shared), Err(LockError::Conflict));
// Once the writer commits and releases, the reader gets in.
lm.release(writer, row).unwrap();
lm.try_acquire(reader, row, LockMode::Shared).unwrap();Range locking, to keep another transaction from inserting into a span you have read:
use lock_db::prelude::*;
let lm = LockManager::new();
let index = ResourceId::new(10); // the key space being protected
// Txn 1 read-locks the key range [100, 200].
lm.try_acquire_range(TxnId::new(1), index, KeyRange::new(100, 200).unwrap(), LockMode::Shared).unwrap();
// Txn 2 cannot write key 150 inside that range.
let conflict = lm.try_acquire_range(TxnId::new(2), index, KeyRange::point(150), LockMode::Exclusive);
assert_eq!(conflict, Err(LockError::Conflict));
// But a disjoint range is free.
lm.try_acquire_range(TxnId::new(2), index, KeyRange::new(201, 300).unwrap(), LockMode::Exclusive).unwrap();Modules§
- prelude
- The crate’s common imports.
Structs§
- KeyRange
- An inclusive range of
u64keys,[start, end]. - Lock
Manager std - A sharded lock table mapping resources to the transactions that hold them.
- Resource
Id - Identifies a lockable resource.
- TxnId
- Identifies the transaction that owns a lock request.