Low-overhead counter and latency-sample store for SuperCache observability.
All state is held in a single :public ETS table owned by this GenServer.
Writes use update_counter/3 (atomic) and are safe to call from any
process without coordination overhead.
Counter keys
Counters are stored as {namespace, field} tuples:
{:tpc, :committed}
{:tpc, :aborted}
{:api, :put}, :calls} # note: nested via get_all/1
{{:api, :put}, :errors}Latency samples
Latency ring buffers are stored as {:latency, key} records holding a
list of the most recent @max_samples microsecond values. The list is
bounded so memory usage is constant regardless of call volume.
Usage
# In Router / Replicator — after an operation completes:
Metrics.increment({:api, :put}, :calls)
Metrics.push_latency({:api_latency_us, :put}, elapsed_us)
# Reading back:
Metrics.get_all({:api, :put})
# => %{calls: 1_200, errors: 2}
Metrics.get_latency_samples({:api_latency_us, :put})
# => [45, 110, 88, ...]
Summary
Functions
Returns a specification to start this module under a supervisor.
Return all counters for namespace as %{field => count}.
Return all latency samples for key (unordered).
Atomically increment counter field under namespace by 1.
Push a latency sample (microseconds) into the ring buffer for key.
Reset all counters and latency samples for namespace.
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
Return all counters for namespace as %{field => count}.
@spec get_latency_samples(term()) :: [non_neg_integer()]
Return all latency samples for key (unordered).
Atomically increment counter field under namespace by 1.
@spec push_latency(term(), non_neg_integer()) :: :ok
Push a latency sample (microseconds) into the ring buffer for key.
The buffer is capped at 256 entries; oldest samples are dropped when the cap is reached.
@spec reset(term()) :: :ok
Reset all counters and latency samples for namespace.