lock-db 0.3.0

Lock manager and deadlock detection for Rust databases - row/range locks, multiple granularities, and wait-for cycle detection.
Documentation

In this release (v0.3.0):

  • Lock modes — the five standard multi-granularity modes — intention-shared (IS), intention-exclusive (IX), shared (S), shared-intention-exclusive (SIX), and exclusive (X) — with a const compatibility matrix at the core of every grant decision
  • Hierarchical granularities — lock a database / table / page / row hierarchy correctly with intention locks; the manager enforces the matrix at every level
  • Range locks — lock a contiguous span of keys (KeyRange) for predicate / phantom protection, with overlap-based conflict detection
  • Sharded lock table — the resource space is partitioned across independent shards so acquisitions on unrelated resources never contend on the same mutex
  • Acquire / release — non-blocking try_acquire, single and bulk release, re-entrant acquisition, and lattice upgrades (e.g. S + IX → SIX)

Planned across the rest of the 0.x series (see dev/ROADMAP.md):

  • Wait / grant queues — fair, blocking acquisition with upgrade handling
  • Deadlock detection — a wait-for graph with cycle detection and configurable victim selection

Installation

[dependencies]
lock-db = "0.3"

Quick Start

use lock_db::prelude::*;

// One manager, shared across all worker threads behind an `Arc`.
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-lock a span of keys to keep another transaction from inserting into it (phantom protection):

use lock_db::prelude::*;

let lm = LockManager::new();
let index = ResourceId::new(10); // the key space being protected

// Txn 1 read-locks keys [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, but a disjoint range is free.
assert!(lm.try_acquire_range(TxnId::new(2), index, KeyRange::point(150), LockMode::Exclusive).is_err());
lm.try_acquire_range(TxnId::new(2), index, KeyRange::new(201, 300).unwrap(), LockMode::Exclusive).unwrap();

A transaction drops its whole lock set — point and range — in one call at commit or abort:

use lock_db::prelude::*;

let lm = LockManager::new();
let txn = TxnId::new(1);
for id in 0..3 {
    lm.try_acquire(txn, ResourceId::new(id), LockMode::Exclusive).unwrap();
}
assert_eq!(lm.release_all(txn), 3);

API Overview

For the complete reference with method tables and examples, see docs/API.md.

Examples

Runnable examples live in examples/. Run any of them with cargo run --example <name>:

Example Shows
quick_start Acquire, conflict, release on a single row.
two_phase_locking Growing-phase acquires, then release_all at commit.
shared_upgrade Read under a shared lock, then upgrade to exclusive.
hierarchy Intention locks over a database/table/page/row hierarchy.
range_locks Range locking for phantom protection.
concurrent Many threads contending on one row, with a mutual-exclusion check.

Where It Fits

lock-db is the concurrency-control layer. It is used by:

  • txn-db — transactions acquire and release locks here to enforce isolation
  • page-db — page-granularity locks coordinate with the paged store
  • index-db — range locks protect B+tree key ranges against phantoms
  • storage engines — any engine needing pessimistic concurrency control

It has no first-party dependencies, so it builds and tests standalone today.

Cross-Platform Support

Linux (x86_64, aarch64), macOS (x86_64, Apple Silicon), and Windows (x86_64) are first-class and verified by the CI matrix.

Contributing

See CONTRIBUTING.md and dev/DIRECTIVES.md. Before a PR: cargo fmt --all, cargo clippy --all-targets --all-features -- -D warnings, and cargo test --all-features must be clean.