defmodule Dust.ActivityBuffer do
@moduledoc """
ETS-backed circular buffer for recent Dust operations.
Stores the last 100 events per store for dashboard display.
Append is a direct ETS write — no GenServer serialization on the hot path.
"""
@max_entries 100
def start_link(opts) do
name = Keyword.fetch!(opts, :name)
case :ets.whereis(name) do
:undefined -> :ets.new(name, [:named_table, :public, :set])
_ref -> :ok
end
:ignore
end
def child_spec(opts) do
%{
id: {__MODULE__, opts[:name]},
start: {__MODULE__, :start_link, [opts]},
type: :worker,
restart: :temporary
}
end
def append(name, store, attrs) do
entry =
Map.merge(attrs, %{
timestamp: DateTime.utc_now(),
store: store
})
# Get and increment the per-store index
idx = :ets.update_counter(name, {:idx, store}, {2, 1}, {{:idx, store}, 0})
slot = rem(idx - 1, @max_entries)
:ets.insert(name, {{store, slot}, entry})
:ok
end
def recent(name, store, limit \\ @max_entries) do
# Read up to @max_entries slots for this store
entries =
for slot <- 0..(@max_entries - 1),
[{_key, entry}] <- [:ets.lookup(name, {store, slot})],
do: entry
entries
|> Enum.sort_by(& &1.seq, :desc)
|> Enum.take(limit)
end
end