Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

This is a Rust crate called `invocation-counter` that provides a data structure to answer: "how many times a function has been called in the last X minutes?" It's focused on concurrency, memory management, and efficient algorithms for tracking invocation counts over time windows.

## Development Commands

### Building
- `cargo build` - Build debug version
- `cargo build --release` - Build release version

### Testing
- `cargo test` - Run all tests

### Code Quality
- `cargo check` - Fast compile check without building
- `cargo fmt` - Format code according to Rust standards
- `cargo clippy` - Run clippy linter for additional checks

### Combined Quality Check
Run all quality checks in sequence (as done in CI):
```bash
cargo check && cargo fmt && cargo clippy
```

## Architecture Notes

### Core Implementation

The main library code is in `src/lib.rs` and implements a lock-free ring buffer design using atomic operations for thread-safe invocation counting.

**Key Components:**

- `InvocationCounter`: The main data structure that tracks invocations over sliding time windows
- `Slot`: Individual ring buffer slots containing atomic counters and interval start times
- Uses `AtomicU32` for counters and `AtomicU64` for timestamps to ensure thread safety

**Algorithm Design:**

- Ring buffer with configurable slot count (2^`slot_count_exp`) and slot size (2^`slot_size_exp`)
- Each slot represents a time interval, with slots reused as time progresses
- Efficient O(slot_count) counting by iterating only through ring buffer slots
- Slots automatically reset when reused for new time intervals

**Public API:**

- `new(slot_count_exp: u8, slot_size_exp: u8)`: Creates counter with 2^slot_count_exp slots, each covering 2^slot_size_exp time units
- `register(current_time: u64)`: Records an invocation at the given timestamp
- `count_in(start_time: u64, end_time: u64)`: Returns total invocations within the specified time range

**Thread Safety:**

The implementation is fully thread-safe using atomic operations with appropriate memory ordering:
- `Acquire`/`Release` ordering for synchronization
- `Relaxed` ordering for performance where strict ordering isn't required
- `compare_exchange_weak` for lock-free max time tracking

**Count Approximation:**

The counter provides approximate counts due to its interval-based design:
- Invocations are grouped into discrete time intervals (slots)
- Time ranges are aligned to slot boundaries for querying
- Recent invocations may be counted even if they fall slightly outside the exact requested range
- This approximation trades perfect accuracy for significant performance and memory efficiency
75 changes: 49 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,43 +1,66 @@
# Invocation counter
# invocation-counter

[![Rust](https://github.com/oramasearch/invocation-counter/actions/workflows/ci.yml/badge.svg)](https://github.com/oramasearch/invocation-counter/actions/workflows/ci.yml)
[![Crates.io](https://img.shields.io/crates/v/invocation-counter.svg)](https://crates.io/crates/invocation-counter)
[![Documentation](https://docs.rs/invocation-counter/badge.svg)](https://docs.rs/invocation-counter)
[![License: Apache-2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)

This data structure responses the following question: how many times a function has been called in the last X minutes?
A high-performance, thread-safe data structure for tracking function invocation counts over sliding time windows. Perfect for rate limiting, monitoring, and analytics use cases.

## Installation
## Quick Start

```text
cargo add invocation-counter
Add this to your `Cargo.toml`:

```toml
[dependencies]
invocation-counter = "*"
```

## Example
## Basic Usage

```rust
use invocation_counter::Counter;
use invocation_counter::InvocationCounter;

fn main() {
// 4 is the group_shift_factor
// 16 is the number of buckets
let counter = Counter::<16, 4>::new(4);
// Create a counter with 8 slots × 16 time units = 128-unit sliding window
let counter = InvocationCounter::new(3, 4); // 2^3 slots, 2^4 time units per slot

let mut now = 0; // Instant::now().elapsed().as_secs();
counter.increment_by_one(now);
// Register function calls
counter.register(10); // Called at time 10
counter.register(25); // Called at time 25
counter.register(50); // Called at time 50

now += 1; // Simulate a second passing
counter.increment_by_one(now);
// Query how many calls in the last 128 time units
assert_eq!(counter.count_in(0, 100), 3); // All calls within range
```

assert_eq!(counter.get_count_till(now), 2);
## Core Concepts

now += 2_u64.pow(4); // Simulate 16 seconds passing
counter.increment_by_one(now);
### Time Units
Time units are abstract - they can represent milliseconds, seconds, minutes, or any consistent measurement. The counter works with `u64` timestamps where larger values represent later points in time.

now += 1; // Simulate a second passing
counter.increment_by_one(now);
### Sliding Windows
The counter tracks invocations within a sliding time window that moves forward with each query. The window size is determined by:

assert_eq!(counter.get_count_till(now), 4);
**Window Size = 2^slot_count_exp × 2^slot_size_exp**

now += 2_u64.pow(4) * 16; // Move foward for a while...
counter.increment_by_one(now);
assert_eq!(counter.get_count_till(now), 1); // The counter should have reset
}
```
### Configuration Parameters

- **`slot_count_exp`**: Exponent for number of slots (2^slot_count_exp total slots)
- **`slot_size_exp`**: Exponent for time units per slot (2^slot_size_exp time units per slot)

**Trade-offs:**
- More slots = better precision, more memory usage
- Larger slot size = longer windows, less precision
- The counter provides approximate counts optimized for performance

## Important Notes

### Approximation
The counter provides **approximate** counts due to its interval-based design:
- Invocations are grouped into discrete time intervals
- Recent invocations may be counted even if slightly outside the exact window
- This approximation enables high performance and fixed memory usage

## License

Licensed under the Apache License, Version 2.0. See LICENSE file for details.
24 changes: 0 additions & 24 deletions examples/readme.rs

This file was deleted.

Loading