Class: Philiprehberger::RateCounter::Counter

Inherits:
Object
  • Object
show all
Defined in:
lib/philiprehberger/rate_counter.rb

Overview

A sliding-window rate counter for measuring event throughput

Examples:

counter = Counter.new(window: 60)
counter.increment
counter.rate  # => events per second

Instance Method Summary collapse

Constructor Details

#initialize(window: 60) ⇒ Counter

Create a new rate counter

Parameters:

  • window (Numeric) (defaults to: 60)

    the sliding window size in seconds

Raises:

  • (Error)

    if window is not positive



27
28
29
30
31
32
33
34
# File 'lib/philiprehberger/rate_counter.rb', line 27

def initialize(window: 60)
  raise Error, 'Window must be positive' unless window.is_a?(Numeric) && window.positive?

  @window = window
  @buckets = []
  @peak_rate = 0.0
  @mutex = Mutex.new
end

Instance Method Details

#countInteger

Get the current event count within the window

Returns:

  • (Integer)

    total events in the current window



53
54
55
56
57
58
# File 'lib/philiprehberger/rate_counter.rb', line 53

def count
  @mutex.synchronize do
    prune
    count_internal
  end
end

#increment(n = 1) ⇒ self

Increment the counter

Parameters:

  • n (Integer) (defaults to: 1)

    number of events to record (default: 1)

Returns:

  • (self)


40
41
42
43
44
45
46
47
48
# File 'lib/philiprehberger/rate_counter.rb', line 40

def increment(n = 1)
  @mutex.synchronize do
    @buckets << [now, n]
    prune
    current_rate = rate_internal
    @peak_rate = current_rate if current_rate > @peak_rate
  end
  self
end

#peak_rateFloat

Get the highest rate per second observed since creation or last reset

Returns:

  • (Float)

    peak events per second



73
74
75
# File 'lib/philiprehberger/rate_counter.rb', line 73

def peak_rate
  @mutex.synchronize { @peak_rate }
end

#rateFloat

Get the rate of events per second

Returns:

  • (Float)

    events per second



63
64
65
66
67
68
# File 'lib/philiprehberger/rate_counter.rb', line 63

def rate
  @mutex.synchronize do
    prune
    rate_internal
  end
end

#rate_per(unit) ⇒ Float

Get the rate projected to a specific time unit

Parameters:

  • unit (Symbol)

    :second, :minute, or :hour

Returns:

  • (Float)

    events per unit

Raises:

  • (Error)

    if unit is unknown



113
114
115
116
117
118
# File 'lib/philiprehberger/rate_counter.rb', line 113

def rate_per(unit)
  multiplier = RATE_UNITS[unit]
  raise Error, "Unknown unit: #{unit}. Use :second, :minute, or :hour" unless multiplier

  rate * multiplier
end

#resetvoid

This method returns an undefined value.

Reset the counter



123
124
125
126
127
128
# File 'lib/philiprehberger/rate_counter.rb', line 123

def reset
  @mutex.synchronize do
    @buckets.clear
    @peak_rate = 0.0
  end
end

#snapshotHash

Return a frozen snapshot of the counter state

Returns:

  • (Hash)

    frozen hash with count, rate, peak_rate, window, and timestamp



80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/philiprehberger/rate_counter.rb', line 80

def snapshot
  @mutex.synchronize do
    prune
    {
      count: count_internal,
      rate: rate_internal,
      peak_rate: @peak_rate,
      window: @window,
      timestamp: Process.clock_gettime(Process::CLOCK_MONOTONIC)
    }.freeze
  end
end

#time_since_lastFloat?

Get the number of seconds since the most recent increment

Returns ‘nil` if the counter has never been incremented or if all events have fully expired from the sliding window.

Returns:

  • (Float, nil)

    seconds since the last increment, or nil if the window is empty



99
100
101
102
103
104
105
106
# File 'lib/philiprehberger/rate_counter.rb', line 99

def time_since_last
  @mutex.synchronize do
    prune
    return nil if @buckets.empty?

    now - @buckets.last[0]
  end
end