Packages
ra
2.16.0-pre.6
3.1.9
3.1.8
3.1.7
3.1.6
3.1.5
3.1.4
3.1.3
3.1.2
3.1.1
3.1.0
3.0.2
3.0.1
3.0.0
3.0.0-beta.1
2.17.3
2.17.2
2.17.1
2.17.0
2.16.13
2.16.12
2.16.11
2.16.10
2.16.9
2.16.8
2.16.7
2.16.6
2.16.5
2.16.4
2.16.3
2.16.2
2.16.1
2.16.0
2.16.0-pre.12
2.16.0-pre.11
2.16.0-pre.10
2.16.0-pre.9
2.16.0-pre.8
2.16.0-pre.7
2.16.0-pre.6
2.16.0-pre.5
2.16.0-pre.4
2.16.0-pre.3
2.16.0-pre.2
2.16.0-pre.1
2.15.4
2.15.3
2.15.2
2.15.1
2.15.0
2.14.0
2.13.6
2.13.5
2.13.4
2.13.3
2.13.2
2.13.1
2.13.0
2.13.0-pre.1
2.12.0
2.11.0
2.11.0-pre.1
2.10.2-pre.2
2.10.2-pre.1
2.10.1
2.10.0
2.10.0-pre.3
2.10.0-pre.2
2.10.0-pre.1
2.9.10-pre.1
2.9.1
2.9.1-pre.2
2.9.1-pre.1
2.9.0
2.8.0
retired
2.7.3
2.7.2
2.7.1
2.7.0
2.7.0-pre.3
2.7.0-pre.2
2.7.0-pre.1
2.6.3
2.6.2
2.6.1
2.6.0-pre.1
2.5.1
2.5.1-pre.1
2.5.0
2.4.9
2.4.8
2.4.7
2.4.6
2.4.5
2.4.4
2.4.3
2.4.2
retired
2.4.1
2.4.0
2.3.0
2.2.0
2.1.0
2.0.13
2.0.12
2.0.11
2.0.10
2.0.9
2.0.8
2.0.7
2.0.6
2.0.5
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
1.1.9
1.1.8
1.1.7
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.8
1.0.7
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.9.6
0.9.5
0.9.4
0.9.2
0.3.3
retired
0.3.2
retired
0.3.1
retired
Raft library
Current section
Files
Jump to
Current section
Files
src/ra_log_ets.erl
%% This Source Code Form is subject to the terms of the Mozilla Public
%% License, v. 2.0. If a copy of the MPL was not distributed with this
%% file, You can obtain one at https://mozilla.org/MPL/2.0/.
%%
%% Copyright (c) 2017-2023 Broadcom. All Rights Reserved. The term Broadcom refers to Broadcom Inc. and/or its subsidiaries.
%%
%% @hidden
-module(ra_log_ets).
-behaviour(gen_server).
-export([start_link/1]).
-export([
mem_table_please/2,
mem_table_please/3,
new_mem_table_please/3,
delete_mem_tables/2,
execute_delete/3]).
-export([init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
terminate/2,
code_change/3]).
-include("ra.hrl").
-record(state, {names :: ra_system:names(),
table_opts :: list()}).
-define(TABLE_OPTS, [set,
{write_concurrency, auto},
public
]).
%%% ra_log_ets - owns and creates mem_table ETS tables
%%%===================================================================
%%% API functions
%%%===================================================================
start_link(#{names := #{log_ets := Name}} = Cfg) ->
gen_server:start_link({local, Name}, ?MODULE, [Cfg], []).
-spec mem_table_please(ra_system:names(), ra:uid()) ->
{ok, ra_mt:state()} | {error, term()}.
mem_table_please(Names, UId) ->
mem_table_please(Names, UId, read_write).
-spec mem_table_please(ra_system:names(), ra:uid(), read | read_write) ->
{ok, ra_mt:state()} | {error, term()}.
mem_table_please(#{log_ets := Name,
open_mem_tbls := OpnMemTbls}, UId, Mode) ->
case ets:lookup(OpnMemTbls, UId) of
[] ->
case gen_server:call(Name, {mem_table_please, UId, #{}}) of
{ok, [Tid | Rem]} ->
Mt = lists:foldl(
fun (T, Acc) ->
ra_mt:init_successor(T, Mode, Acc)
end, ra_mt:init(Tid, Mode), Rem),
{ok, Mt};
Err ->
Err
end;
[{_, Tid} | Rem] ->
Mt = lists:foldl(
fun ({_, T}, Acc) ->
ra_mt:init_successor(T, Mode, Acc)
end, ra_mt:init(Tid, Mode), Rem),
{ok, Mt}
end.
-spec new_mem_table_please(ra_system:names(), ra:uid(), ra_mt:state()) ->
{ok, ra_mt:state()} | {error, term()}.
new_mem_table_please(#{log_ets := Name}, UId, Prev) ->
case gen_server:call(Name, {new_mem_table_please, UId, #{}}, infinity) of
{ok, Tid} ->
{ok, ra_mt:init_successor(Tid, read_write, Prev)};
Err ->
Err
end.
delete_mem_tables(#{log_ets := Name}, UId) ->
gen_server:cast(Name, {delete_mem_tables, UId}).
-spec execute_delete(ra_system:names(),
ra:uid(),
ra_mt:delete_spec()) ->
ok.
execute_delete(#{}, _UId, undefined) ->
ok;
execute_delete(#{log_ets := Name}, UId, Spec) ->
gen_server:cast(Name, {exec_delete, UId, Spec}).
%%%===================================================================
%%% gen_server callbacks
%%%===================================================================
init([#{data_dir := DataDir,
name := System,
names := #{log_ets := LogEts,
open_mem_tbls := OpenMemTbls} = Names} = Config]) ->
process_flag(trap_exit, true),
CompressMemTbls = maps:get(compress_mem_tables, Config, false),
TblOpts = ?TABLE_OPTS ++ [{compressed, CompressMemTbls}],
?INFO("~s: in system ~s initialising. Mem table opts: ~w",
[LogEts, System, TblOpts]),
_ = ets:new(OpenMemTbls, [bag, protected, named_table]),
ok = ra_directory:init(DataDir, Names),
{ok, #state{names = Names,
table_opts = TblOpts}}.
handle_call({mem_table_please, UId, Opts}, _From,
#state{names = #{open_mem_tbls := OpnMemTbls}} = State) ->
case ets:lookup(OpnMemTbls, UId) of
[] ->
Name = maps:get(table_name, Opts, ra_mem_table),
Tid = ets:new(Name, ?TABLE_OPTS),
true = ets:insert(OpnMemTbls, {UId, Tid}),
{reply, {ok, [Tid]}, State};
Tids ->
{reply, {ok, Tids}, State#state{}}
end;
handle_call({new_mem_table_please, UId, Opts}, _From,
#state{names = #{open_mem_tbls := OpnMemTbls}} = State) ->
Name = maps:get(table_name, Opts, ra_mem_table),
Tid = ets:new(Name, ?TABLE_OPTS),
true = ets:insert(OpnMemTbls, {UId, Tid}),
{reply, {ok, Tid}, State};
handle_call(Request, _From, State) ->
?INFO("ra_log_ets:handle_call/3 unhandled request ~w", [Request]),
Reply = ok,
{reply, Reply, State}.
handle_cast({exec_delete, UId, Spec},
#state{names = #{open_mem_tbls := MemTables}} = State) ->
case Spec of
{delete, Tid} ->
ets:delete_object(MemTables, {UId, Tid});
_ ->
ok
end,
%% TODO: delete from ra_log_open_mem_tables if {delete, tid()}
try timer:tc(fun () -> ra_mt:delete(Spec) end) of
{Time, Num} ->
?DEBUG("ra_log_ets: ra_mt:delete/1 took ~bms to delete ~w ~b entries",
[Time div 1000, Spec, Num]),
ok
catch
_:Err ->
?WARN("ra_log_ets: failed to delete ~w ~w ",
[Spec, Err]),
ok
end,
{noreply, State};
handle_cast({delete_mem_tables, UId},
#state{names = #{open_mem_tbls := MemTables}} = State) ->
%% delete ets tables,
%% we need to be defensive here.
%% it is better to leak a table than to crash them all
Objects = ets:take(MemTables, UId),
[begin
try timer:tc(fun () -> ets_delete(Tid) end) of
{Time, true} ->
?DEBUG("ra_log_ets: ets:delete/1 took ~bms to delete ~w",
[Time div 1000, Tid]),
ok
catch
_:Err ->
?WARN("ra_log_ets: failed to delete ets table ~w with ~w "
"This table may need to be cleaned up manually",
[Tid, Err]),
ok
end
end || {_, Tid} <- Objects],
{noreply, State};
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info(_Info, State) ->
{noreply, State}.
ets_delete(Tid) ->
_ = ets:delete(Tid),
true.
terminate(Reason, #state{names = Names}) ->
?DEBUG("ra_log_ets: terminating with ~p", [Reason]),
ok = ra_directory:deinit(Names),
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.