ElixirScope.Capture.RingBuffer (elixir_scope v0.0.1)
High-performance lock-free ring buffer for event ingestion.
Uses :atomics for lock-free operations and :persistent_term for metadata storage. Designed for >100k events/sec throughput with bounded memory usage.
Key features:
- Lock-free writes using atomic compare-and-swap
- Bounded memory with configurable overflow behavior
- Multiple reader support with position tracking
- Minimal allocation overhead
- Graceful degradation under extreme load
Summary
Functions
Clears all events from the buffer and resets counters.
Destroys the buffer and cleans up resources.
Creates a new ring buffer with the specified configuration.
Reads the next available event from the buffer.
Reads multiple events in batch for better throughput.
Returns the buffer size.
Gets current buffer statistics.
Writes an event to the ring buffer.
Types
@type buffer_id() :: atom()
@type overflow_strategy() :: :drop_oldest | :drop_newest | :block
@type position() :: non_neg_integer()
@type t() :: %ElixirScope.Capture.RingBuffer{ atomics_ref: :atomics.atomics_ref(), buffer_table: :ets.tab(), id: buffer_id(), mask: non_neg_integer(), overflow_strategy: overflow_strategy(), size: pos_integer() }
Functions
@spec clear(t()) :: :ok
Clears all events from the buffer and resets counters.
@spec destroy(t()) :: :ok
Destroys the buffer and cleans up resources.
Creates a new ring buffer with the specified configuration.
Options
:size
- Buffer size (must be power of 2, default: 1024):overflow_strategy
- What to do when buffer is full (default: drop_oldest):name
- Optional name for the buffer (default: generates unique name)
Examples
iex> {:ok, buffer} = RingBuffer.new(size: 1024)
iex> RingBuffer.size(buffer)
1024
Reads the next available event from the buffer.
Returns {:ok, event, new_position}
or :empty
if no events available.
@spec read_batch(t(), position(), pos_integer()) :: {[ElixirScope.Events.event()], position()}
Reads multiple events in batch for better throughput.
Returns {events, new_position}
where events is a list of up to count
events.
@spec size(t()) :: pos_integer()
Returns the buffer size.
@spec stats(t()) :: %{ size: pos_integer(), write_position: position(), read_position: position(), available_events: non_neg_integer(), total_writes: non_neg_integer(), total_reads: non_neg_integer(), dropped_events: non_neg_integer(), utilization: float() }
Gets current buffer statistics.
@spec write(t(), ElixirScope.Events.event()) :: :ok | {:error, :buffer_full}
Writes an event to the ring buffer.
This is the critical hot path - optimized for minimal latency. Target: <1µs per write operation.
Examples
iex> {:ok, buffer} = RingBuffer.new()
iex> event = %Events.FunctionExecution{function: :test}
iex> :ok = RingBuffer.write(buffer, event)