%% 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).