diff --git a/README.org b/README.org index 587099d44..90a67a9fa 100644 --- a/README.org +++ b/README.org @@ -1,48 +1,21 @@ * basho_bench ** Overview - [[http://travis-ci.org/basho/basho_bench][Travis-CI]] :: [[https://secure.travis-ci.org/basho/basho_bench.png]] - [[http://docs.basho.com/riak/latest/cookbooks/Benchmarking/][Additional documentation on docs.basho.com]] + [[https://github.com/basho/basho_bench/blob/master/README.org][For general information about basho_bench, please refer to the original README]] Basho Bench is a benchmarking tool created to conduct accurate and repeatable performance tests and stress tests, and produce performance graphs. + +** Modification - Originally developed to benchmark Riak, it exposes a pluggable - driver interface and has been extended to serve as a benchmarking - tool across a variety of projects. - - Basho Bench focuses on two metrics of performance: - - - Throughput: number of operations performed in a timeframe, - captured in aggregate across all operation types - - Latency: time to complete single operations, captured in - quantiles per-operation - -** Quick Start - - You must have [[http://erlang.org/download.html][Erlang/OTP R13B03]] or later to build and run Basho - Bench, and [[http://www.r-project.org/][R]] to generate graphs of your benchmarks. A sane - GNU-style build system is also required if you want to use =make= - to build the project. - -#+BEGIN_SRC shell -git clone git://github.com/basho/basho_bench.git -cd basho_bench -make all -#+END_SRC - - This will build an executable script, =basho_bench=, which you can - use to run one of the existing benchmark configurations from the - =examples/= directory. You will likely have to make some minor directory - changes to the configs in order to get the examples running (see, e.g., the - source of the bitcask and innostore benchmark config files for direction). + Two files are added for benchmarking floppystore: + - src/basho_bench_driver_floppystore.erl: defines the initialization of a benchmarking thread and how it executes put/get operations. + - examples/floppstore.config: contains benchmark parameters. +** Run a benchmark #+BEGIN_SRC shell -$ ./basho_bench examples/riakc_pb.config -INFO: Est. data size: 95.37 MB -INFO: Using target ip {127,0,0,1} for worker 1 -INFO: Starting max worker: <0.55.0> +$ ./basho_bench examples/floppystore.config #+END_SRC At the end of the benchmark, results will be available in CSV @@ -51,99 +24,9 @@ INFO: Starting max worker: <0.55.0> #+BEGIN_SRC shell $ make results -priv/summary.r -i tests/current -Loading required package: proto -Loading required package: reshape -Loading required package: plyr -Loading required package: digest -null device - 1 $ open tests/current/summary.png #+END_SRC -** Troubleshooting Graph Generation - - If make results fails with the error =/usr/bin/env: Rscript --vanilla: No such file or directory= - please edit priv/summary.r and replace the first line with the full path to the Rscript binary on your system - - If you receive the error message =Warning: unable to access index for repository http://lib.stat.cmu.edu/R/CRAN/src/contrib= - it means the default R repo for installing additional packages is broken, you can change it as follows: - -#+BEGIN_SRC shell -$ R -> chooseCRANmirror() -Selection: 69 -quit() -make results -#+END_SRC - -** Customizing your Benchmark - Basho Bench has many drivers, each with its own configuration, and - a number of key and value generators that you can use to customize - your benchmark. It is also straightforward -- with less than 200 - lines of Erlang code -- to create custom drivers that can exercise - other systems or perform custom operations. These are covered more - in detail in the [[http://docs.basho.com/riak/latest/cookbooks/Benchmarking/][documentation]]. - -** Benchmarking with riak-java-client - The [[https://github.com/basho/riak-java-client][riak-java-client]] can be used to benchmark a Riak cluster. There - is an example configuration in =examples/riakc_java.config=. You - will need the [[https://github.com/basho/bench_shim][bench_shim]] project. You will also need to uncomment - and edit the following line in basho_bench's =rebar.config=, adding - your own erlang cookie value: - -#+BEGIN_SRC shell -%% {escript_emu_args, "%%! -name bb@127.0.0.1 -setcookie YOUR_ERLANG_COOKIE\n"}. -#+END_SRC - -** Alternative Graph Generation by gnuplot - You can generate graphs using gnuplot. - -#+BEGIN_SRC shell -$ ./priv/gp_throughput.sh -#+END_SRC - -#+BEGIN_SRC shell -$ ./priv/gp_latency.sh -#+END_SRC - - By passing =-h= option to each script, help messages are shown. - - Some of options for these scripts are: - - - =-d TEST_DIR= : comma separated list of directories which include - test result CSV files - - =-t TERMINAL_TYPE= : gnuplot terminal type - - =-P= : just print gnuplot script without drawing graph - - For example, you can draw graphs with ASCII characters - by the option =-t dumb=, which is useful in non-graphical - environment or quick sharing of result in chat. - - Also, you can plot multiple test runs on a single plot by using "-d" switch. - -** Contributing - We encourage contributions to Basho Bench from the community. - - 1) Fork the =basho_bench= repository on [[https://github.com/basho/basho_bench][Github]]. - 2) Clone your fork or add the remote if you already have a clone of - the repository. -#+BEGIN_SRC shell -git clone git@github.com:yourusername/basho_bench.git -# or -git remote add mine git@github.com:yourusername/basho_bench.git -#+END_SRC - 3) Create a topic branch for your change. -#+BEGIN_SRC shell -git checkout -b some-topic-branch -#+END_SRC - 4) Make your change and commit. Use a clear and descriptive commit - message, spanning multiple lines if detailed explanation is - needed. - 5) Push to your fork of the repository and then send a pull-request - through Github. -#+BEGIN_SRC shell -git push mine some-topic-branch -#+END_SRC - 6) A Basho engineer or community maintainer will review your patch - and merge it into the main repository or send you feedback. +** Limitations + Right now it only has put/get interfaces and there is no support for transaction. + It only put/get for riak_dt_gcounter. diff --git a/examples/floppstore.config b/examples/floppstore.config new file mode 100644 index 000000000..2e9df2f4b --- /dev/null +++ b/examples/floppstore.config @@ -0,0 +1,26 @@ +{mode, max}. + +{duration, 1}. + +{concurrent, 1}. + +{driver, basho_bench_driver_floppystore}. + +{key_generator, {uniform_int, 50000}}. + +{value_generator, {fixed_bin, 10}}. + +%%{operations, [{append, 1}, {read, 1}]}. +{operations, [{stx, 1}, {itx, 1}]}. + %%[{append, 1}, {read, 1}]}. +%%[{stx, 1}, {itx,1}]}. %% , {append, 1}, {read, 1}]}. + +%% the second element in the list below (e.g., "../../public/bitcask") must point to +%% the relevant directory of a bitcask installation +{code_paths, ["../floppystore/ebin"]}. + +{floppystore_nodes, ['floppy@127.0.0.1']}. +{floppystore_cookie, floppy}. + +{floppystore_mynode, ['floppy_bench@127.0.0.1', longnames]}. +{floppystore_types, [{riak_dt_gcounter, [increment]}, {riak_dt_gset, [add]}]}. diff --git a/src/basho_bench_driver_floppystore.erl b/src/basho_bench_driver_floppystore.erl new file mode 100644 index 000000000..93a265ea7 --- /dev/null +++ b/src/basho_bench_driver_floppystore.erl @@ -0,0 +1,211 @@ +%% ------------------------------------------------------------------- +%% +%% basho_bench: Benchmarking Suite +%% +%% Copyright (c) 2009-2010 Basho Techonologies +%% +%% This file is provided to you under the Apache License, +%% Version 2.0 (the "License"); you may not use this file +%% except in compliance with the License. You may obtain +%% a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% +%% ------------------------------------------------------------------- +-module(basho_bench_driver_floppystore). + +-export([new/1, + run/4]). + +-include("basho_bench.hrl"). + +-define(TIMEOUT, 1000). +-record(state, {node, + worker_id, + time, + type_dict}). + %key_dict}). + +%% ==================================================================== +%% API +%% ==================================================================== + +new(Id) -> + %% Make sure bitcask is available + case code:which(floppy) of + non_existing -> + ?FAIL_MSG("~s requires floppystore to be available on code path.\n", + [?MODULE]); + _ -> + ok + end, + + Nodes = basho_bench_config:get(floppystore_nodes), + Cookie = basho_bench_config:get(floppystore_cookie), + MyNode = basho_bench_config:get(floppystore_mynode, [basho_bench, longnames]), + Types = basho_bench_config:get(floppystore_types), + + %% Try to spin up net_kernel + case net_kernel:start(MyNode) of + {ok, _} -> + ?INFO("Net kernel started as ~p\n", [node()]); + {error, {already_started, _}} -> + ok; + {error, Reason} -> + ?FAIL_MSG("Failed to start net_kernel for ~p: ~p\n", [?MODULE, Reason]) + end, + + %% Initialize cookie for each of the nodes + [true = erlang:set_cookie(N, Cookie) || N <- Nodes], + %true = erlang:set_cookie(Node, Cookie), + + %% Try to ping each of the nodes + ping_each(Nodes), + + %% Choose the node using our ID as a modulus + TargetNode = lists:nth((Id rem length(Nodes)+1), Nodes), + ?INFO("Using target node ~p for worker ~p\n", [TargetNode, Id]), + %KeyDict= dict:new(), + TypeDict = dict:from_list(Types), + {ok, #state{node=TargetNode, time={1,1,1}, worker_id=Id, type_dict=TypeDict}}. + +%% @doc Read a key +run(read, KeyGen, _ValueGen, State=#state{node=Node}) -> + Key = KeyGen(), + KeyType = get_key_type(Key), + BinaryKey = Key, %%<<(Key):32/big>>, + Res = rpc:call(Node, floppy, read, [BinaryKey, KeyType], ?TIMEOUT), + case Res of + {ok, _Value} -> + {ok, State}; + {error, Reason} -> + {error, Reason}; + {badrpc, Reason} -> + {badrpc, Reason}; + _Reason -> + {ok, State} + end; +%% @doc Write to a key +run(append, KeyGen, ValueGen, State=#state{node=Node, worker_id=Id, type_dict=TypeDict}) -> + Key = KeyGen(), + Type = get_key_type(Key), + BinaryKey = Key, %%<<(Key):32/big>>, + {Type, KeyParam} = get_random_param(TypeDict, Type, Id, ValueGen()), + Res = rpc:call(Node, floppy, append, [BinaryKey, Type, KeyParam], ?TIMEOUT), + case Res of + {ok, _Result} -> + {ok, State}; + {error, Reason} -> + {error, Reason}; + {badrpc, Reason} -> + {badrpc, Reason}; + _ -> + {ok, State} + end; +%% @doc Start a static transaction +run(stx, KeyGen, ValueGen, State=#state{node=Node, time=ClientTime, worker_id=Id, type_dict=Dict}) -> + random:seed(now()), + NumAppend = random:uniform(10), + NumRead = random:uniform(10), + ListAppends = get_random_append_ops(NumAppend, Dict, Id, KeyGen, ValueGen), + ListReads = get_random_read_ops(NumRead, KeyGen), + ListOps = ListAppends++ListReads, + Res = rpc:call(Node, floppy, clocksi_execute_tx, [ClientTime, ListOps], ?TIMEOUT), + case Res of + {ok, _ReadSet, CommitTime} -> + {ok, State#state{time=CommitTime}}; + {error, Reason} -> + {error, Reason}; + {badrpc, Reason} -> + {badrpc, Reason}; + _Reason -> + {ok, State} + end; +run(itx, KeyGen, ValueGen, State=#state{node=Node, worker_id=Id, type_dict=Dict}) -> + random:seed(now()), + NumAppend = random:uniform(10), + NumRead = random:uniform(10), + ListAppends = get_random_append_ops(NumAppend, Dict, Id, KeyGen, ValueGen), + ListReads = get_random_read_ops(NumRead, KeyGen), + ListOps = ListAppends++ListReads, + RandomOps = [X||{_,X} <- lists:sort([ {random:uniform(), N} || N <- ListOps])], + {ok, TxId} = rpc:call(Node, floppy, clocksi_istart_tx, [now()], ?TIMEOUT), + ExecuteFun = fun(X) -> case X of + {update, UKey, UType, UParam} -> ok = rpc:call(Node, floppy, clocksi_iupdate, [TxId, UKey, UType, UParam]); + {read, RKey, RType} -> {ok, _} = rpc:call(Node, floppy, clocksi_iread, [TxId, RKey, RType]) + end + end, + lists:foreach(ExecuteFun, RandomOps), + {ok, _} = rpc:call(Node, floppy, clocksi_iprepare, [TxId]), + End=rpc:call(Node, floppy, clocksi_icommit, [TxId]), + case End of + {ok, _} -> + {ok, State}; + {error, Reason} -> + {error, Reason}; + {badrpc, Reason} -> + {badrpc, Reason}; + _Reason -> + {ok, State} + end. + + + +%% Private +ping_each([]) -> + ok; +ping_each([Node | Rest]) -> + case net_adm:ping(Node) of + pong -> + ?INFO("Finished pinging ~p", [Node]), + ping_each(Rest); + pang -> + ?FAIL_MSG("Failed to ping node ~p\n", [Node]) + end. + +get_key_type(Key) -> + Rem = Key rem 10, + case Rem > 5 of + true -> + riak_dt_gcounter; + false -> + riak_dt_gset + end. + +get_random_param(Dict, Type, Actor, Value) -> + Params = dict:fetch(Type, Dict), + random:seed(now()), + Num = random:uniform(length(Params)), + case Type of + riak_dt_gcounter -> + {riak_dt_gcounter, {lists:nth(Num, Params), Actor}}; + riak_dt_gset -> + {riak_dt_gset, {{lists:nth(Num, Params), Value}, Actor}} + end. + + +get_random_append_op(Key, Dict, Actor, Value) -> + Type = get_key_type(Key), + {Type, Params} = get_random_param(Dict, Type, Actor, Value), + {update, Key, Type, Params}. + +get_random_read_op(Key) -> + Type = get_key_type(Key), + {read, Key, Type}. + +get_random_append_ops(Num, Dict, Actor, KeyGen, ValueGen) -> + %KeyList = [<<(KeyGen()):32/big>> || _ <- lists:seq(1, Num)], + KeyList = [KeyGen() || _ <- lists:seq(1, Num)], + [get_random_append_op(Key, Dict, Actor, ValueGen()) || Key <- KeyList]. + +get_random_read_ops(Num, KeyGen) -> + %KeyList = [<<(KeyGen()):32/big>> || _ <- lists:seq(1, Num)], + KeyList = [KeyGen() || _ <- lists:seq(1, Num)], + [get_random_read_op(Key) || Key <- KeyList].