Current section
Files
Jump to
Current section
Files
src/memstore_gc.erl
%% Copyright (c) 2017-present. Benoit Chesneau
%%
%% This source code is licensed under the MIT license found in the
%% LICENSE file in the root directory of this source tree.
-module(memstore_gc).
-author("benoitc").
%% API
-export([start_link/3]).
-export([
init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
code_change/3,
terminate/2
]).
-include_lib("stdlib/include/ms_transform.hrl").
start_link(Name, Tab, Interval) ->
gen_server:start_link(?MODULE, [Name, Tab, Interval], []).
init([Name, Tab, Interval]) ->
erlang:send_after(Interval, self(), timeout),
{ok, #{name => Name, tab => Tab, interval => Interval}}.
handle_call(_Msg, _From, State) -> {reply, ok, State}.
handle_cast(_Msg, State) -> {noreply, State}.
handle_info(timeout, State = #{ name := Name, tab := Tab, interval := Interval }) ->
ok = do_gc(Name, Tab),
erlang:send_after(Interval, self(), timeout),
{noreply, State}.
terminate(_Reason, _Config) -> ok.
code_change(_Vsn, State, _Extra) -> {ok, State}.
min_version(Name) ->
LastSeq = memstore_versions:last_sequence(Name),
MS = ets:fun2ms(
fun({{lock, V}, _}) -> V end
),
case ets:select(Name, MS, 1) of
{[V], _} when V /= LastSeq -> erlang:min(V, LastSeq);
_ -> LastSeq
end.
do_gc(Name, Tab) ->
case min_version(Name) of
1 -> ok;
Min ->
MS = ets:fun2ms(
fun({{K, S}, _V}) when S =< Min -> {K, S} end
),
traverse(ets:select(Tab, MS, 1), Tab)
end.
traverse('$end_of_table', _) -> ok;
traverse({[Prev], Cont}, Tab) ->
traverse(ets:select(Cont), Prev, Tab).
traverse('$end_of_table', _, _) -> ok;
traverse({[{Key, _}=Prev], Cont}, {Key, Seq}, Tab) ->
_ = ets:delete(Tab, {Key, Seq}),
traverse(ets:select(Cont), Prev, Tab);
traverse({[Prev], Cont}, _, Tab) ->
traverse(ets:select(Cont), Prev, Tab).