philiprehberger-rate_window
Time-windowed rate tracker with configurable resolution
Requirements
- Ruby >= 3.1
Installation
Add to your Gemfile:
gem "philiprehberger-rate_window"
Or install directly:
gem install philiprehberger-rate_window
Usage
require "philiprehberger/rate_window"
tracker = Philiprehberger::RateWindow.new(window: 60, resolution: 1)
tracker.record(1)
tracker.record(5)
tracker.record(3)
tracker.rate # => events per second over the window
tracker.sum # => 9.0
tracker.count # => 3
tracker.average # => 3.0
Percentiles
tracker = Philiprehberger::RateWindow.new(window: 60, resolution: 1)
100.times { |i| tracker.record(i) }
tracker.percentile(50) # => median value (with linear interpolation)
tracker.percentile(95) # => 95th percentile
tracker.percentile(99) # => 99th percentile
tracker.median # => shortcut for percentile(50)
tracker.p95 # => shortcut for percentile(95)
Multiple quantiles in one pass
Compute several quantiles together (values are sorted only once per call):
tracker.quantiles(0.25, 0.5, 0.75, 0.95)
# => { 0.25 => 4.0, 0.5 => 7.5, 0.75 => 10.0, 0.95 => 18.2 }
Fractions must be between 0.0 and 1.0 inclusive. An empty tracker returns 0.0 for each requested fraction.
Min / Max
tracker = Philiprehberger::RateWindow.new(window: 60, resolution: 1)
tracker.record(5)
tracker.record(20)
tracker.record(3)
tracker.min # => 3.0
tracker.max # => 20.0
Spread statistics
tracker = Philiprehberger::RateWindow.new(window: 60, resolution: 0.001)
[2, 4, 4, 4, 5, 5, 7, 9].each do |v|
tracker.record(v)
sleep(0.002)
end
tracker.variance # => 4.0 (population variance)
tracker.stddev # => 2.0 (population standard deviation)
An empty tracker returns 0.0 for both. A single value also returns 0.0.
Histogram
tracker = Philiprehberger::RateWindow.new(window: 60, resolution: 1)
100.times { |i| tracker.record(i) }
tracker.histogram(buckets: 5)
# => [
# { range: 0.0..20.0, count: ... },
# { range: 20.0..40.0, count: ... },
# ...
# ]
Custom Resolution
# 5-minute window with 10-second buckets
tracker = Philiprehberger::RateWindow.new(window: 300, resolution: 10)
tracker.record(42)
tracker.rate # => rate per second over 5 minutes
Snapshot
Get all stats atomically in a single call (one mutex acquisition, one cleanup pass):
tracker = Philiprehberger::RateWindow.new(window: 60, resolution: 1)
tracker.record(10)
tracker.record(20)
tracker.record(30)
tracker.snapshot
# => {
# sum: 60.0,
# count: 3,
# rate: 1.0,
# average: 20.0,
# min: 10.0,
# max: 30.0,
# median: 20.0,
# p95: 28.0,
# variance: 66.666...,
# stddev: 8.164...
# }
All values reflect the same instant. An empty tracker returns 0.0 for all numeric fields and 0 for count.
Reset
tracker.reset
tracker.sum # => 0.0
tracker.count # => 0
API
| Method | Description |
|---|---|
.new(window:, resolution:) |
Create a tracker with window (seconds) and bucket resolution |
#record(value = 1) |
Record a value in the current time bucket |
#rate |
Calculate rate per second over the window |
#sum |
Sum of all values in the window |
#count |
Number of recordings in the window |
#average |
Average value per recording |
#percentile(p) |
Calculate percentile (0-100) with linear interpolation |
#median |
Shortcut for percentile(50) |
#p95 |
Shortcut for percentile(95) |
#quantiles(*fractions) |
Hash mapping each fraction (0.0–1.0) to its percentile value in one pass |
#min |
Minimum recorded value in the window |
#max |
Maximum recorded value in the window |
#variance |
Population variance of values in the window |
#stddev |
Population standard deviation of values in the window |
#histogram(buckets: 10) |
Value distribution as array of { range:, count: } hashes |
#snapshot |
Atomic hash of all stats: sum, count, rate, average, min, max, median, p95, variance, stddev |
#reset |
Clear all recorded data |
Development
bundle install
bundle exec rspec
bundle exec rubocop
Support
If you find this project useful: