Counters
Conditional & batch operations
Atomic compare-and-write and ordered batch operations on counters.
Both counters support conditional writes and batches. All of them are atomic on the Redis side, so there is no read-modify-write window for another instance to slip through.
Conditional writes
inc_if and set_if apply the change only when the current value satisfies a CounterComparator. They return (new, old); when the condition fails, new == old and nothing was written.
use distkit::CounterComparator;
counter.set(&key, 10).await?;
// 10 == 10 -> applies. Returns (15, 10).
assert_eq!(counter.inc_if(&key, CounterComparator::Eq(10), 5).await?, (15, 10));
// 15 is not < 10 -> no-op. Returns (15, 15).
assert_eq!(counter.inc_if(&key, CounterComparator::Lt(10), 5).await?, (15, 15));
// Nil always matches. Returns (20, 15).
assert_eq!(counter.inc_if(&key, CounterComparator::Nil, 5).await?, (20, 15));
Batch operations
Batches act on many keys in a single call and preserve input order in the result.
// Read several keys at once.
let snapshot = counter.get_all(&[&k1, &k2, &k3]).await?;
// -> Vec<(&DistkitRedisKey, i64)>
// Increment several at once.
let updated = counter.inc_all(&[(&k1, 1), (&k2, 5)]).await?;
// -> Vec<(&DistkitRedisKey, i64)> (key, new)
Conditional batches
inc_all_if and set_all_if combine the two: each entry carries its own comparator, and each result is (key, new, old).
counter.set(&k1, 10).await?;
let results = counter
.inc_all_if(&[
(&k1, CounterComparator::Eq(10), 5),
(&k2, CounterComparator::Nil, 2),
])
.await?;
assert_eq!(results, vec![(&k1, 15, 10), (&k2, 2, 0)]);
Here k1 matched (10 == 10) and became 15; k2 started at 0 and Nil always applies, so it became 2. As with single conditional writes, an entry whose comparison fails comes back with new == old.

