Current section
Files
Jump to
Current section
Files
src/evoq_checkpoint_store_ets.erl
%% @doc ETS-based checkpoint store implementation.
%%
%% In-memory checkpoint storage for development and testing.
%% For production, use a persistent implementation (e.g., database-backed).
%%
%% Note: Checkpoints are lost on application restart.
%% Use only for development or projections that can easily rebuild.
%%
%% @author rgfaber
-module(evoq_checkpoint_store_ets).
-behaviour(evoq_checkpoint_store).
-behaviour(gen_server).
%% API
-export([start_link/0]).
%% Behavior callbacks
-export([load/1, save/2, delete/1]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2]).
-define(SERVER, ?MODULE).
-define(TABLE, evoq_checkpoints).
%%====================================================================
%% API
%%====================================================================
%% @doc Start the checkpoint store.
-spec start_link() -> {ok, pid()} | {error, term()}.
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
%% @doc Load checkpoint for a projection.
-spec load(atom()) -> {ok, non_neg_integer()} | {error, not_found}.
load(ProjectionName) ->
case ets:lookup(?TABLE, ProjectionName) of
[{ProjectionName, Checkpoint}] ->
{ok, Checkpoint};
[] ->
{error, not_found}
end.
%% @doc Save checkpoint for a projection.
-spec save(atom(), non_neg_integer()) -> ok.
save(ProjectionName, Checkpoint) ->
true = ets:insert(?TABLE, {ProjectionName, Checkpoint}),
ok.
%% @doc Delete checkpoint for a projection.
-spec delete(atom()) -> ok.
delete(ProjectionName) ->
true = ets:delete(?TABLE, ProjectionName),
ok.
%%====================================================================
%% gen_server callbacks
%%====================================================================
%% @private
init([]) ->
%% Create ETS table for checkpoints
?TABLE = ets:new(?TABLE, [
named_table,
public,
set,
{read_concurrency, true},
{write_concurrency, true}
]),
{ok, #{}}.
%% @private
handle_call(_Request, _From, State) ->
{reply, {error, unknown_request}, State}.
%% @private
handle_cast(_Msg, State) ->
{noreply, State}.
%% @private
handle_info(_Info, State) ->
{noreply, State}.
%% @private
terminate(_Reason, _State) ->
ok.