Expand description
A simple and easy-to-use asynchronous redis distributed read-write lock implementation based on tokio and redis-rs.
It supports the following features:
- Read-write mutual exclusion: Only one write lock or multiple read locks can exist at the same time.
- Passive release: When the lock fails to be unlocked due to network or abnormal exit, the lock will be automatically released after the specified timeout.
- Automatic extension: After the lock is successfully locked, a future will be spawned to automatically extend the lock passive timeout until the lock is actively released. (If the program exits abnormally and the lock is not actively released, the automatic extension will also be terminated and the lock will automatically expire and be released).
§Examples
- General usage
use rds_lock::{Locker, Mode};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let cli = redis::Client::open("redis://127.0.0.1:6379/0")?;
let con = redis::aio::ConnectionManager::new(cli).await?;
let key = "lock_key".into();
// Lock key with default write mode, and
// the passive timeout will be automatically extend
// util this lock unlocked.
let w_unlock = Locker::new(con.clone()).lock(key.clone()).await?;
// Do something with lock guard.
for x in 1..10 {
println!("{}", x);
}
// When the key is locked in write mode, the other write or read lock should fail.
assert!(Locker::new(con.clone()).mode(&Mode::W).lock(key.clone()).await.is_err());
assert!(Locker::new(con.clone()).mode(&Mode::R).lock(key.clone()).await.is_err());
// Explicit unlock is required.
// In most cases you should ignore the unlock result.
let _ = w_unlock.await?;
Ok(())
}
- Future closure usage
use rds_lock::{Locker, Mode};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let cli = redis::Client::open("redis://127.0.0.1:6379/0")?;
let con = redis::aio::ConnectionManager::new(cli).await?;
let key = "lock_key".into();
// Do something in closure with lock guard, no explicit unlock needed.
// If the lock is successful, lock_exec finally returns
// the return value of the closure.
let r = Locker::new(con.clone()).lock_exec(key.clone(), async {
for x in 1..10 {
println!("{}", x);
}
Ok(())
}).await;
r
}
- Custom execution parameters
use rds_lock::{Locker, Mode};
#[tokio::main]
async fn main() -> anyhow::Result<i32> {
let cli = redis::Client::open("redis://127.0.0.1:6379/0")?;
let con = redis::aio::ConnectionManager::new(cli).await?;
let key = "lock_key".into();
let locker = Locker::new(con)
.mode(&Mode::R) // Set lock mode, default write-mode.
.to(2000) // Set milliseconds of lock passive timeout, default 3000.
.rty_int(200) // Set milliseconds of retry lock interval milliseconds, default 100.
.rty_to(1500) // Set milliseconds of retry lock timeout milliseconds, default 1000, if set to -1, means retry never timed out.
.ext_int(800); // Set milliseconds of extend lock interval, default 1000.
// Do something with lock guard
locker.lock_exec(key, async {
let mut r = 0;
for x in 1..10 {
r += x;
}
Ok(r)
})
}
Modules§
Structs§
- Locker
- Locker provides an easy-to-use lock builder and bundles the function of automatic passive timeout extend.
Enums§
- Mode
- Lock mode
Functions§
- extend
- Extend passive timeout duration of the specified lock with its key and id. It returns a not found error when the key or id not matched or the lock has been unlocked. Note: For an expired read lock, if there are other valid read locks, it can still be extended successfully.
- lock
- Set a lock with the specified key, returns the lock id. If the key is exclusive with the other locks, it will be retried util timed out.
- unlock
- Unlock the specified lock with its key and id. It returns a not found error when the key or id not matched or the lock has been unlocked. Note: For an expired read lock, if there are other valid read locks, it can still be unlocked successfully.