LaxCounter
LaxCounter trades immediate consistency for speed. Increments accumulate in a local DashMap and are flushed to Redis in batched pipelines every allowed_lag (default ~20 ms). The hot path never blocks on the network, so writes are effectively free.
Use it for analytics, telemetry, and high-frequency metrics where a few milliseconds of cross-process lag is acceptable.
Construct
use distkit::{DistkitRedisKey, counter::{CounterOptions, LaxCounter, CounterTrait}};
let prefix = DistkitRedisKey::try_from("my_app".to_string())?;
let counter = LaxCounter::new(CounterOptions::new(prefix, conn));
Like the strict counter, new returns an Arc. Constructing it spawns a background flush task.
Reads see your own writes
let key = DistkitRedisKey::try_from("impressions".to_string())?;
counter.inc(&key, 1).await?; // local atomic add - sub-microsecond
let val = counter.get(&key).await?; // local view: remote_total + pending_delta
get returns remote_total + pending_delta, so within the same process a read always includes increments you haven't flushed yet. Across processes, another instance won't see your increment until the next flush lands.
Tuning the lag
allowed_lag on CounterOptions controls the flush interval. Lower it for tighter consistency, raise it to batch more aggressively:
use std::time::Duration;
let mut options = CounterOptions::new(prefix, conn);
options.allowed_lag = Duration::from_millis(50);
let counter = LaxCounter::new(options);
The background task cleans up after itself
The flush task holds a Weak reference to the counter. When the last Arc<LaxCounter> is dropped, the task notices and stops - there is nothing to shut down manually.
set, del, clear stay immediate
set, del, and clear are not buffered; they apply right away (after the pending delta is accounted for). Only inc/dec ride the buffer.

