distkit
Counters

Overview

Distributed integer counters in strict and lax flavors, both behind one trait.

Counters are distkit's default feature. They are distributed i64 counters keyed by a DistkitRedisKey, stored in a Redis hash under the prefix you give them.

There are two implementations, and they share the same interface:

  • StrictCounter - every call is atomic and immediately consistent.
  • LaxCounter - increments buffer locally and flush on an interval.

Pick between them based on the strict vs lax trade-off.

The shared trait

Both implement CounterTrait. Bring it into scope to call any of these:

pub trait CounterTrait {
    async fn inc(&self, key: &DistkitRedisKey, count: i64) -> Result<i64, DistkitError>;
    async fn dec(&self, key: &DistkitRedisKey, count: i64) -> Result<i64, DistkitError>;
    async fn get(&self, key: &DistkitRedisKey) -> Result<i64, DistkitError>;
    async fn set(&self, key: &DistkitRedisKey, count: i64) -> Result<i64, DistkitError>;
    async fn del(&self, key: &DistkitRedisKey) -> Result<i64, DistkitError>;
    async fn clear(&self) -> Result<(), DistkitError>;

    // Conditional - return (new, old).
    async fn inc_if(&self, key: &DistkitRedisKey, cmp: CounterComparator, count: i64)
        -> Result<(i64, i64), DistkitError>;
    async fn set_if(&self, key: &DistkitRedisKey, cmp: CounterComparator, count: i64)
        -> Result<(i64, i64), DistkitError>;

    // Batch - preserve input order.
    async fn get_all(&self, keys: &[&DistkitRedisKey]) -> Result<Vec<(&DistkitRedisKey, i64)>, DistkitError>;
    async fn inc_all(&self, updates: &[(&DistkitRedisKey, i64)]) -> Result<Vec<(&DistkitRedisKey, i64)>, DistkitError>;
    async fn set_all(&self, updates: &[(&DistkitRedisKey, i64)]) -> Result<Vec<(&DistkitRedisKey, i64)>, DistkitError>;
    async fn inc_all_if(&self, updates: &[(&DistkitRedisKey, CounterComparator, i64)])
        -> Result<Vec<(&DistkitRedisKey, i64, i64)>, DistkitError>;
    async fn set_all_if(&self, updates: &[(&DistkitRedisKey, CounterComparator, i64)])
        -> Result<Vec<(&DistkitRedisKey, i64, i64)>, DistkitError>;
}

Constructing

Both take CounterOptions and return an Arc:

use distkit::{DistkitRedisKey, counter::{CounterOptions, StrictCounter}};

let prefix = DistkitRedisKey::try_from("my_app".to_string())?;
let options = CounterOptions::new(prefix, conn); // conn: ConnectionManager

let counter = StrictCounter::new(options); // Arc<StrictCounter>

CounterOptions also carries allowed_lag (default ~20 ms), which only matters for the lax counter's flush interval.

Read on