ash-lock
Distributed locking with fencing tokens for async Rust. It uses an in-process backend by default, and etcd for locking across processes.
Disclaimer: this repository contains AI-generated code.
Features
LockClient, a fluentLockBuilder, andLockGuard, which releases onDropor with an explicitunlock().await.- Fencing tokens: a strictly increasing token per resource key, handed out on every acquire.
- Watchdog: renews the lease in the background and poisons the guard if renewal fails.
- Backends:
InMemoryBackend(default, single process) andEtcdBackend(theetcdfeature). ImplementLockBackendfor anything else. - Causal stamps: acquisitions can be stamped with a Hybrid Logical Clock
from
ash-time(theash-timefeature).
Installation
# for locking across processes over etcd
Quick start
use ;
use Duration;
async
async
Fencing tokens
A distributed lock can't stop a stalled holder (a GC pause, a paused VM) from waking up after its lease has expired and been granted to someone else, and then writing stale data. The lock alone doesn't give mutual exclusion on the resource.
The defence is to attach the fencing token to every write and have the resource reject any write whose token is lower than the highest it has seen:
use validate_write;
assert!; // current holder: accept
assert!; // newer holder: accept and raise the watermark
assert!; // stale holder: reject
If the watchdog fails to renew a lease, the guard is poisoned and
fencing_token() returns Err(Error::LeaseExpired), so a holder that has lost
its lease can't authorise further writes. A renewal failure poisons the guard
instead of panicking, so a network partition can't take down unrelated tasks
on the same runtime.
Using etcd
use ;
use Arc;
let backend = new;
let client = connect_with.await?;
An acquire grants an etcd lease for the TTL and creates the lock key, bound to
that lease, in a transaction that only succeeds if the key doesn't exist. The
commit revision becomes the fencing token, so tokens increase across the whole
cluster and survive restarts. Renewal uses lease keep-alives, and release
revokes the lease. etcd lease TTLs have whole-second granularity. Building with
etcd requires protoc.
Cargo features
| Feature | Default | Enables |
|---|---|---|
etcd |
no | EtcdBackend, distributed locking over etcd v3 |
ash-time |
no | Hybrid Logical Clock stamps on acquisitions |
Current limitations
with_fairness(true)only affects client-side retry pacing. There is no server-side FIFO wait queue.LockConfig::cluster_seedis recorded but not used for discovery.- Lock metrics are kept in the in-crate
telemetrymodule and aren't exported anywhere yet.
Testing
# integration test against a live etcd at localhost:2379
Minimum supported Rust version
Rust 1.88 or later.
Contributing
See CONTRIBUTING.md. To report a security issue, see SECURITY.md.
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or https://opensource.org/licenses/MIT)
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.