Bounded circular buffer for latency statistics calculation.
Maintains a fixed-size buffer of latency samples and provides percentile calculations (p50, p99) for request/response timing.
Uses :queue for O(1) insertion with eviction of oldest samples
when capacity is reached. Percentile calculation requires sorting
(O(n log n)) but operates on bounded data.
Telemetry Integration
This module is designed to work with ZenWebsocket's telemetry events, providing aggregated latency metrics from individual measurements.
Summary
Functions
Adds a latency sample in milliseconds to the buffer.
Creates a new latency stats buffer with configurable max size.
Calculates the specified percentile from buffered samples.
Returns a summary map with p50, p99, last sample, and count.
Types
@type t() :: %ZenWebsocket.LatencyStats{ count: non_neg_integer(), max_size: pos_integer(), samples: :queue.queue(non_neg_integer()) }
Functions
@spec add(t(), non_neg_integer()) :: t()
Adds a latency sample in milliseconds to the buffer.
Evicts the oldest sample if the buffer is at capacity.
Examples
iex> stats = ZenWebsocket.LatencyStats.new(max_size: 3)
iex> stats = ZenWebsocket.LatencyStats.add(stats, 10)
iex> stats = ZenWebsocket.LatencyStats.add(stats, 20)
iex> stats.count
2
Creates a new latency stats buffer with configurable max size.
Options
max_size- Maximum number of samples to retain (default: 100)
Examples
iex> stats = ZenWebsocket.LatencyStats.new()
iex> stats.max_size
100
iex> stats = ZenWebsocket.LatencyStats.new(max_size: 50)
iex> stats.max_size
50
@spec percentile(t(), number()) :: non_neg_integer() | nil
Calculates the specified percentile from buffered samples.
Returns nil if the buffer is empty.
Examples
iex> stats = ZenWebsocket.LatencyStats.new()
iex> stats = Enum.reduce(1..100, stats, &ZenWebsocket.LatencyStats.add(&2, &1))
iex> ZenWebsocket.LatencyStats.percentile(stats, 50)
50
iex> ZenWebsocket.LatencyStats.percentile(ZenWebsocket.LatencyStats.new(), 50)
nil
@spec summary(t()) :: %{ p50: non_neg_integer(), p99: non_neg_integer(), last: non_neg_integer(), count: non_neg_integer() } | nil
Returns a summary map with p50, p99, last sample, and count.
Returns nil if the buffer is empty.
Examples
iex> stats = ZenWebsocket.LatencyStats.new()
iex> stats = Enum.reduce([10, 20, 30, 40, 50], stats, &ZenWebsocket.LatencyStats.add(&2, &1))
iex> summary = ZenWebsocket.LatencyStats.summary(stats)
iex> summary.count
5
iex> summary.last
50