hashtree-lmdb 0.2.83

LMDB-backed content-addressed blob storage for hashtree
Documentation
# hashtree-lmdb

LMDB-backed content-addressed blob storage for hashtree.

High-performance storage backend using LMDB (Lightning Memory-Mapped Database) for fast key-value storage.

## Usage

```rust
use hashtree_core::Store;
use hashtree_lmdb::{compute_sha256, LmdbBlobStore};

let store = LmdbBlobStore::new("/path/to/data")?;
let data = b"immutable blob".to_vec();
let hash = compute_sha256(&data);

assert!(store.put(hash, data.clone()).await?);
assert!(!store.put(hash, data.clone()).await?); // idempotent

assert_eq!(store.get(&hash).await?, Some(data));
```

## Multiprocess sharing

Trusted processes running as the same OS user may open one store path on a local
filesystem directly; no storage daemon is required. LMDB serializes writers and
exposes only committed transactions to readers. Blob insertion uses `NO_OVERWRITE`,
so concurrent writes of one correctly computed hash are idempotent. An uncommitted
transaction is rolled back if its process dies. This guarantee does not extend to
network filesystems or to a separate application's mutable metadata databases.

Processes sharing Hashtree's application store should not construct
`LmdbBlobStore` from `data_dir/blobs` themselves. Use the canonical opener with
the same configured storage budget as the application:

```rust
use hashtree_lmdb::open_shared_lmdb_blob_store;
use std::sync::Arc;

let store = Arc::new(open_shared_lmdb_blob_store(data_dir, max_size_bytes)?);
```

The returned `ConfiguredLmdbBlobStore` implements `Store`. It owns the configured
hot and legacy LMDB environments as one composite store, including their distinct
external-blob directories. The helper also owns the `data_dir/blobs` path, map-size
floor, environment flags, and deterministic external-marker fallback, so consumers
do not duplicate those choices. All sharing processes must still receive identical
`HTREE_LMDB_*` environment overrides and `max_size_bytes`; change those options only
while the other processes are stopped.

The immutable blob invariant is narrower than the mutable application state:

- Callers must provide the SHA-256 key for the bytes they write. The backend does
  not recompute it. Retrieval layers must hash-verify bytes before returning or
  caching them.
- Access order, aggregate statistics, pin counts, and garbage collection changes
  are transactional shared state. Pin counts are durable application references,
  not process leases: a process dying does not undo a committed pin.
- `max_bytes` is configured on each store handle. Processes allowed to run garbage
  collection must use one agreed quota policy because eviction affects every
  process sharing the path. Quota decisions read the shared persisted totals rather
  than a process-local byte count.
- Every process using external blobs or packs must use the canonical opener so
  marker paths and hot/legacy tier ownership remain identical.

All simultaneously running processes must open the environment with the same,
sufficient map size. If another process enlarges the map after a process has opened
it, that older handle reports `MDB_MAP_RESIZED`; it must exit and reopen before
accessing the enlarged environment. Establish a larger map before starting the
other processes. The default map size is fixed and intentionally large, so ordinary
same-host sharing does not require runtime resizing.

## Features

- Memory-mapped I/O for fast reads
- ACID transactions
- Multiprocess-safe committed reads and idempotent blob writes
- Crash recovery for unfinished LMDB transactions

Part of [hashtree-rs](https://git.iris.to/#/npub1xdhnr9mrv47kkrn95k6cwecearydeh8e895990n3acntwvmgk2dsdeeycm/hashtree).