diff options
Diffstat (limited to 'lib/mnesia/examples/bench/bench.erl')
-rw-r--r-- | lib/mnesia/examples/bench/bench.erl | 327 |
1 files changed, 327 insertions, 0 deletions
diff --git a/lib/mnesia/examples/bench/bench.erl b/lib/mnesia/examples/bench/bench.erl new file mode 100644 index 0000000000..d191169296 --- /dev/null +++ b/lib/mnesia/examples/bench/bench.erl @@ -0,0 +1,327 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% File : bench.hrl +%%% Author : Hakan Mattsson <[email protected]> +%%% Purpose : Implement the Canadian database benchmark (LMC/UU-01:025) +%%% Created : 21 Jun 2001 by Hakan Mattsson <[email protected]> +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-module(bench). +-author('[email protected]'). + +-include("bench.hrl"). + +-export([ + run/0, run/1, + + start_all/0, start_all/1, + populate/0, populate/1, + generate/0, generate/1, + + args_to_config/1, verify_config/2, + start/0, start/1, + stop_slave_nodes/1, + bind_schedulers/0 + ]). + +bind_schedulers() -> + try + %% Avoid first core and bind schedules to the remaining ones + Topo = erlang:system_info(cpu_topology), + erlang:system_flag(cpu_topology,lists:reverse(Topo)), + %% N = erlang:system_info(schedulers), + %% erlang:system_flag(schedulers_online, lists:max([N - 1, 1])), + erlang:system_flag(scheduler_bind_type, default_bind), + timer:sleep(timer:seconds(1)), % Wait for Rickard + erlang:system_info(scheduler_bindings) + catch _:_ -> + %% Ancient systems + ignore + end. + +%% Run the benchmark: +%% +%% - Start all necessary Erlang nodes +%% - Populate the database +%% - Start the traffic generators +%% - Calculate benchmark statistics +%% - Stop the temporary Erlang nodes +run() -> + FileName = "bench.config", + run([FileName]). + +run(Args) -> + C = args_to_config(Args), + SlaveNodes = start_all(C), + bench_populate:start(C), + Result = bench_generate:start(C), + stop_slave_nodes(SlaveNodes), + Result. + +%% Start Mnesia on the local node +start() -> + FileName = 'bench.config', + start([FileName]). + +start(Args) -> + C = args_to_config(Args), + erlang:set_cookie(node(), C#config.cookie), + Nodes = [node() | (((C#config.table_nodes -- C#config.generator_nodes) ++ + C#config.generator_nodes) -- [node()])], + Extra = [{extra_db_nodes, Nodes}], + ?d("Starting Mnesia on node ~p...", [node()]), + case mnesia:start(Extra) of + ok -> + Tables = mnesia:system_info(tables), + io:format(" ok.~n" , []), + ?d("Waiting for ~p tables...", [length(Tables)]), + wait(Tables); + {error, Reason} -> + io:format(" FAILED: ~p~n", [Reason]), + {error, Reason} + end. + +wait(Tables) -> + case mnesia:wait_for_tables(Tables, timer:seconds(10)) of + ok -> + io:format(" loaded.~n", []), + ok; + {timeout, More} -> + io:format(" ~p...", [length(More)]), + wait(More) + end. + +%% Populate the database +populate() -> + FileName = 'bench.config', + populate([FileName]). + +populate(Args) -> + C = args_to_config(Args), + bench_populate:start(C). + +%% Start the traffic generators +generate() -> + FileName = 'bench.config', + generate([FileName]). + +generate(Args) -> + C = args_to_config(Args), + bench_generate:start(C). + +start_all() -> + FileName = 'bench.config', + start_all([FileName]). + +start_all(Args) -> + C = args_to_config(Args), + Nodes = [node() | (((C#config.table_nodes -- C#config.generator_nodes) ++ + C#config.generator_nodes) -- [node()])], + erlang:set_cookie(node(), C#config.cookie), + ?d("Starting Erlang nodes...~n", []), + ?d("~n", []), + SlaveNodes = do_start_all(Nodes, [], C#config.cookie), + Extra = [{extra_db_nodes, Nodes}], + ?d("~n", []), + ?d("Starting Mnesia...", []), + case rpc:multicall(Nodes, mnesia, start, [Extra]) of + {Replies, []} -> + case [R || R <- Replies, R /= ok] of + [] -> + io:format(" ok~n", []), + SlaveNodes; + Bad -> + io:format(" FAILED: ~p~n", [Bad]), + exit({mnesia_start, Bad}) + end; + Bad -> + io:format(" FAILED: ~p~n", [Bad]), + exit({mnesia_start, Bad}) + end. + +do_start_all([Node | Nodes], Acc, Cookie) when is_atom(Node) -> + case string:tokens(atom_to_list(Node), [$@]) of + [Name, Host] -> + Arg = lists:concat(["-setcookie ", Cookie]), + ?d(" ~s", [left(Node)]), + case slave:start_link(Host, Name, Arg) of + {ok, Node} -> + load_modules(Node), + rpc:call(Node, ?MODULE, bind_schedulers, []), + io:format(" started~n", []), + do_start_all(Nodes, [Node | Acc], Cookie); + {error, {already_running, Node}} -> + rpc:call(Node, ?MODULE, bind_schedulers, []), + io:format(" already started~n", []), + do_start_all(Nodes, Acc, Cookie); + {error, Reason} -> + io:format(" FAILED:~p~n", [Reason]), + stop_slave_nodes(Acc), + exit({slave_start_failed, Reason}) + end; + _ -> + ?d(" ~s FAILED: " + "Not valid as node name. Must be 'name@host'.~n", + [left(Node)]), + stop_slave_nodes(Acc), + exit({bad_node_name, Node}) + end; +do_start_all([], StartedNodes, _Cookie) -> + StartedNodes. + +load_modules(Node) -> + Fun = + fun(Mod) -> + case code:get_object_code(Mod) of + {_Module, Bin, Fname} -> + rpc:call(Node, code,load_binary,[Mod,Fname,Bin]); + Other -> + Other + end + end, + lists:foreach(Fun, [bench, bench_generate, bench_populate, bench_trans]). + +stop_slave_nodes([]) -> + ok; +stop_slave_nodes(Nodes) -> + ?d("~n", []), + ?d("Stopping Erlang nodes...~n", []), + ?d("~n", []), + do_stop_slave_nodes(Nodes). + +do_stop_slave_nodes([Node | Nodes]) -> + ?d(" ~s", [left(Node)]), + Res = slave:stop(Node), + io:format(" ~p~n", [Res]), + do_stop_slave_nodes(Nodes); +do_stop_slave_nodes([]) -> + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% The configuration +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +args_to_config(C) when is_record(C, config) -> + C; +args_to_config(Args) when is_list(Args) -> + do_args_to_config(Args, []). + +do_args_to_config([{Key, Val} | Rest], Acc) when is_list(Acc) -> + do_args_to_config(Rest, Acc ++ [{Key, Val}]); +do_args_to_config([FileName | Rest], Acc) when is_list(Acc) -> + io:nl(), + ?d("Reading configuration file ~p...", [FileName]), + case file:consult(FileName) of + {ok, Config} -> + io:format(" ok~n", []), + do_args_to_config(Rest, Acc ++ Config); + {error, Reason} -> + io:format(" FAILED: ~s~n", + [[lists:flatten(file:format_error( Reason))]]), + {error, {args_to_config, FileName, Reason}} + end; +do_args_to_config([], Acc) when is_list(Acc) -> + verify_config(Acc, #config{}). + +verify_config([{Tag, Val} | T], C) -> + case Tag of + cookie when is_atom(Val) -> + verify_config(T, C#config{cookie = Val}); + generator_profile when Val == random -> + verify_config(T, C#config{generator_profile = Val}); + generator_profile when Val == t1 -> + verify_config(T, C#config{generator_profile = Val}); + generator_profile when Val == t2 -> + verify_config(T, C#config{generator_profile = Val}); + generator_profile when Val == t3 -> + verify_config(T, C#config{generator_profile = Val}); + generator_profile when Val == t4 -> + verify_config(T, C#config{generator_profile = Val}); + generator_profile when Val == t5 -> + verify_config(T, C#config{generator_profile = Val}); + generator_profile when Val == ping -> + verify_config(T, C#config{generator_profile = Val}); + generator_nodes when is_list(Val) -> + verify_config(T, C#config{generator_nodes = Val}); + n_generators_per_node when is_integer(Val), Val >= 0 -> + verify_config(T, C#config{n_generators_per_node = Val}); + generator_warmup when is_integer(Val), Val >= 0 -> + verify_config(T, C#config{generator_warmup = Val}); + generator_duration when is_integer(Val), Val >= 0 -> + verify_config(T, C#config{generator_duration = Val}); + generator_cooldown when is_integer(Val), Val >= 0 -> + verify_config(T, C#config{generator_cooldown = Val}); + statistics_detail when Val == debug -> + verify_config(T, C#config{statistics_detail = Val}); + statistics_detail when Val == debug2 -> + verify_config(T, C#config{statistics_detail = Val}); + statistics_detail when Val == normal -> + verify_config(T, C#config{statistics_detail = Val}); + table_nodes when is_list(Val) -> + verify_config(T, C#config{table_nodes = Val}); + use_binary_subscriber_key when Val == true -> + verify_config(T, C#config{use_binary_subscriber_key = Val}); + use_binary_subscriber_key when Val == false -> + verify_config(T, C#config{use_binary_subscriber_key = Val}); + storage_type when is_atom(Val) -> + verify_config(T, C#config{storage_type = Val}); + write_lock_type when Val == sticky_write -> + verify_config(T, C#config{write_lock_type = Val}); + write_lock_type when Val == write -> + verify_config(T, C#config{write_lock_type = Val}); + n_replicas when is_integer(Val), Val >= 0 -> + verify_config(T, C#config{n_replicas = Val}); + n_fragments when is_integer(Val), Val >= 0 -> + verify_config(T, C#config{n_fragments = Val}); + n_subscribers when is_integer(Val), Val >= 0 -> + verify_config(T, C#config{n_subscribers = Val}); + n_groups when is_integer(Val), Val >= 0 -> + verify_config(T, C#config{n_groups = Val}); + n_servers when is_integer(Val), Val >= 0 -> + verify_config(T, C#config{n_servers = Val}); + always_try_nearest_node when Val == true; Val == false -> + verify_config(T, C#config{always_try_nearest_node = Val}); + _ -> + ?e("Bad config value: ~p~n", [Tag, Val]), + exit({bad_config_value, {Tag, Val}}) + end; +verify_config([], C) -> + display_config(C), + C; +verify_config(Config, _) -> + ?e("Bad config: ~p~n", [Config]), + exit({bad_config, Config}). + +display_config(C) when is_record(C, config) -> + ?d("~n", []), + ?d("Actual configuration...~n", []), + ?d("~n", []), + Fields = record_info(fields, config), + [config | Values] = tuple_to_list(C), + display_config(Fields, Values). + +display_config([F | Fields], [V | Values]) -> + ?d(" ~s ~p~n", [left(F), V]), + display_config(Fields, Values); +display_config([], []) -> + ?d("~n", []), + ok. + +left(Term) -> + string:left(lists:flatten(io_lib:format("~p", [Term])), 27, $.). |