├── README
├── rebar
├── .hgignore
├── Makefile
├── examples
├── null_test.config
├── casbench.config
├── httpraw.config
├── bitcask.config
├── riakc_pb.config
├── riakclient.config
└── innostore_test.config
├── rebar.config
├── include
└── basho_bench.hrl
├── docs
├── pdf-style.org
├── html-style.org
└── Documentation.org
├── priv
├── common.r
├── compare.r
└── summary.r
├── src
├── basho_bench_driver_null.erl
├── basho_bench_driver_dets.erl
├── basho_bench_config.erl
├── basho_bench_app.erl
├── basho_bench_sup.erl
├── basho_bench_valgen.erl
├── basho_bench_driver_innostore.erl
├── basho_bench_driver_cassandra.erl
├── basho_bench_driver_bitcask.erl
├── basho_bench_log.erl
├── basho_bench_keygen.erl
├── basho_bench_driver_riakclient.erl
├── basho_bench_driver_riakc_pb.erl
├── basho_bench.erl
├── basho_bench_stats.erl
├── basho_bench_driver_http_raw.erl
└── basho_bench_worker.erl
├── ebin
└── basho_bench.app
└── LICENSE
/README:
--------------------------------------------------------------------------------
1 | Please see docs/Documentation.org.
2 |
--------------------------------------------------------------------------------
/rebar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/b/basho_bench/master/rebar
--------------------------------------------------------------------------------
/.hgignore:
--------------------------------------------------------------------------------
1 | .beam
2 | tests/.*$
3 | basho_bench$
4 | erl_crash.dump$
5 | deps/.*$
6 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: deps
2 |
3 | all: deps
4 | ./rebar compile escriptize
5 |
6 | deps:
7 | ./rebar get-deps
8 |
9 | clean:
10 | @./rebar clean
11 |
12 | distclean: clean
13 | @rm -rf basho_bench deps
14 |
15 | results:
16 | priv/summary.r -i tests/current
17 |
--------------------------------------------------------------------------------
/examples/null_test.config:
--------------------------------------------------------------------------------
1 | {mode, max}.
2 |
3 | {duration, 5}.
4 |
5 | {concurrent, 1}.
6 |
7 | {driver, basho_bench_driver_null}.
8 |
9 | {code_paths, ["deps/stats"]}.
10 |
11 | {key_generator, {sequential_int_bin, 5000000}}.
12 |
13 | {value_generator, {fixed_bin, 10248}}.
14 |
15 | {operations, [{put, 1}]}.
16 |
--------------------------------------------------------------------------------
/examples/casbench.config:
--------------------------------------------------------------------------------
1 | {mode, max}.
2 |
3 | {duration, 1}.
4 |
5 | {concurrent, 10}.
6 |
7 | {driver, basho_bench_driver_cassandra}.
8 |
9 | {key_generator, {uniform_int_str, 5000000}}.
10 |
11 | {value_generator, {fixed_bin, 10000}}.
12 |
13 | {operations, [{get, 1}, {put, 1}]}.
14 |
15 | {code_paths, ["deps/stats",
16 | "deps/ibrowse",
17 | "deps/casbench"]}.
18 |
--------------------------------------------------------------------------------
/examples/httpraw.config:
--------------------------------------------------------------------------------
1 | {mode, max}.
2 |
3 | {duration, 1}.
4 |
5 | {concurrent, 3}.
6 |
7 | {driver, basho_bench_driver_http_raw}.
8 |
9 | %{code_paths, ["deps/stats",
10 | % "deps/ibrowse"]}.
11 |
12 | {key_generator, {external, test, hoss_seq, [10000, 10, 10, 100]}}.
13 |
14 | {value_generator, {fixed_bin, 10000}}.
15 |
16 | {http_raw_port, 8091}.
17 |
18 | {operations, [{update, 1}]}.
19 |
20 | {source_dir, "foo"}.
21 |
--------------------------------------------------------------------------------
/examples/bitcask.config:
--------------------------------------------------------------------------------
1 | {mode, max}.
2 |
3 | {duration, 10}.
4 |
5 | {concurrent, 1}.
6 |
7 | {driver, basho_bench_driver_bitcask}.
8 |
9 | {key_generator, {uniform_int_bin, 5000000}}.
10 |
11 | {value_generator, {fixed_bin, 10000}}.
12 |
13 | {operations, [{get, 1}, {put, 1}]}.
14 |
15 | {code_paths, ["deps/stats",
16 | "../../public/bitcask"]}.
17 |
18 | {bitcask_dir, "/tmp/bitcask.bench"}.
19 |
20 | {bitcask_flags, [o_sync]}.
21 |
--------------------------------------------------------------------------------
/examples/riakc_pb.config:
--------------------------------------------------------------------------------
1 | {mode, max}.
2 |
3 | {duration, 10}.
4 |
5 | {concurrent, 1}.
6 |
7 | {driver, basho_bench_driver_riakc_pb}.
8 |
9 | {code_paths, ["deps/stats",
10 | "deps/riakc",
11 | "deps/protobuffs"]}.
12 |
13 | {key_generator, {uniform_int_str, 10000}}.
14 |
15 | {value_generator, {fixed_bin, 10000}}.
16 |
17 | {riakc_pb_ips, [{127,0,0,1}]}.
18 |
19 | {riakc_pb_replies, 1}.
20 |
21 | {operations, [{get, 1}, {update, 1}]}.
22 |
23 |
--------------------------------------------------------------------------------
/rebar.config:
--------------------------------------------------------------------------------
1 | {deps, [
2 | {stats, "3", {hg, "http://bitbucket.org/dizzyd/stats", "tip"}},
3 | {ibrowse, "1.*", {git, "http://github.com/dizzyd/ibrowse.git", "HEAD"}},
4 | {casbench, "0.1", {hg, "http://bitbucket.org/basho/casbench", "tip"}},
5 | {riakc, ".*", {hg, "http://bitbucket.org/basho/riak-erlang-client", "tip"}},
6 | {protobuffs, ".*", {hg, "http://bitbucket.org/basho/protobuffs", "tip"}}
7 | ]}.
8 |
9 | {escript_incl_apps, [stats, ibrowse, riakc, protobuffs]}.
10 |
--------------------------------------------------------------------------------
/include/basho_bench.hrl:
--------------------------------------------------------------------------------
1 |
2 |
3 | -define(FAIL_MSG(Str, Args), ?ERROR(Str, Args), halt(1)).
4 |
5 | -define(CONSOLE(Str, Args), basho_bench_log:log(console, Str, Args)).
6 |
7 | -define(DEBUG(Str, Args), basho_bench_log:log(debug, Str, Args)).
8 | -define(INFO(Str, Args), basho_bench_log:log(info, Str, Args)).
9 | -define(WARN(Str, Args), basho_bench_log:log(warn, Str, Args)).
10 | -define(ERROR(Str, Args), basho_bench_log:log(error, Str, Args)).
11 |
12 | -define(FMT(Str, Args), lists:flatten(io_lib:format(Str, Args))).
13 |
14 |
--------------------------------------------------------------------------------
/examples/riakclient.config:
--------------------------------------------------------------------------------
1 | {mode, max}.
2 |
3 | {duration, 15}.
4 |
5 | {concurrent, 5}.
6 |
7 | {driver, basho_bench_driver_riakclient}.
8 |
9 | {code_paths, ["deps/stats",
10 | "/Users/jmeredith/basho/riak/apps/riak_kv",
11 | "/Users/jmeredith/basho/riak/apps/riak_core"]}.
12 |
13 | {key_generator, {uniform_int_bin, 35000}}.
14 |
15 | {value_generator, {fixed_bin, 10000}}.
16 |
17 | {riakclient_nodes, ['riak@127.0.0.1']}.
18 |
19 | {riakclient_mynode, ['riak_bench@127.0.0.1', longnames]}.
20 |
21 | {riakclient_replies, 1}.
22 |
--------------------------------------------------------------------------------
/examples/innostore_test.config:
--------------------------------------------------------------------------------
1 | {mode, max}.
2 |
3 | {duration, 10}.
4 |
5 | {concurrent, 1}.
6 |
7 | {driver, basho_bench_driver_innostore}.
8 |
9 | {code_paths, ["deps/stats",
10 | "../innostore"]}.
11 |
12 | {key_generator, {uniform_int_bin, 500000}}.
13 |
14 | {value_generator, {fixed_bin, 10000}}.
15 |
16 | {innostore_config, [
17 | {data_home_dir, "/tmp/innodb"},
18 | {log_group_home_dir, "/tmp/innodb"},
19 | {buffer_pool_size, 1073741824}, %% 1G of Buffer
20 | {log_files_in_group, 4},
21 | {log_file_size, 134217728} %% 128 MB log files
22 | ]}.
23 |
24 | {operations, [{put, 1}]}.
25 |
--------------------------------------------------------------------------------
/docs/pdf-style.org:
--------------------------------------------------------------------------------
1 | #+LANGUAGE: en
2 | #+LATEX_HEADER: \usepackage{color}
3 | #+LATEX_HEADER: \usepackage{sectsty}
4 | #+LATEX_HEADER: \usepackage{listings}
5 | #+LATEX_HEADER: \usepackage[T1]{fontenc}
6 | #+LATEX_HEADER: \usepackage{cmbright}
7 | #+LATEX_HEADER: \usepackage[left=1in,top=1in,right=1in,bottom=1in, nohead]{geometry}
8 | #+LATEX_HEADER: \usepackage{hyperref}
9 | #+LATEX_HEADER: \setlength\parindent{0in}
10 | #+LATEX_HEADER: \setlength\parskip{0.1in}
11 | #+LATEX_HEADER:\sectionfont{\pagebreak\fontfamily{iwona}\Huge\selectfont}
12 | #+LATEX_HEADER:\subsectionfont{\fontfamily{iwona}\color[rgb]{0.18,0.41,0.56}\selectfont}
13 | #+LATEX_HEADER:\hypersetup{pdfborder={0 0 0 0}, colorlinks=true, linkcolor=[rgb]{0.80,0.44,0.02},urlcolor=[rgb]{0.80,0.44,0.02}}
14 | #+STYLE:
15 | #+OPTIONS: H:3 toc:2 num:t
16 |
--------------------------------------------------------------------------------
/priv/common.r:
--------------------------------------------------------------------------------
1 |
2 | # Load a library, or attempt to install it if it's not available
3 | load_library <- function(Name)
4 | {
5 | if (!library(Name, character.only = TRUE, logical.return = TRUE))
6 | {
7 | install.packages(Name, repos = "http://lib.stat.cmu.edu/R/CRAN")
8 | }
9 | }
10 |
11 | # Load a latency file and ensure that it is appropriately tagged
12 | load_latency_frame <- function(File)
13 | {
14 | op <- strsplit(basename(File), "_")[[1]][1]
15 | frame <- read.csv(File)
16 | frame$op = rep(op, nrow(frame))
17 | return (frame)
18 | }
19 |
20 | # Load summary and latency information for a given directory
21 | load_benchmark <- function(Dir)
22 | {
23 | ## Load up summary data
24 | summary <- read.csv(sprintf("%s/%s", Dir, "summary.csv"))
25 |
26 | ## Get a list of latency files
27 | latencies <- lapply(list.files(path = Dir, pattern = "_latencies.csv",
28 | full.names = TRUE),
29 | load_latency_frame)
30 | latencies <- do.call('rbind', latencies)
31 |
32 | ## Convert timing information in latencies from usecs -> msecs
33 | latencies[4:10] <- latencies[4:10] / 1000
34 |
35 | return (list(summary = summary, latencies = latencies))
36 | }
37 |
38 | load_library("getopt")
39 | load_library("grid")
40 | load_library("ggplot2")
41 |
--------------------------------------------------------------------------------
/docs/html-style.org:
--------------------------------------------------------------------------------
1 | #+STYLE:
39 |
--------------------------------------------------------------------------------
/src/basho_bench_driver_null.erl:
--------------------------------------------------------------------------------
1 | %% -------------------------------------------------------------------
2 | %%
3 | %% basho_bench: Benchmarking Suite
4 | %%
5 | %% Copyright (c) 2009-2010 Basho Techonologies
6 | %%
7 | %% This file is provided to you under the Apache License,
8 | %% Version 2.0 (the "License"); you may not use this file
9 | %% except in compliance with the License. You may obtain
10 | %% a copy of the License at
11 | %%
12 | %% http://www.apache.org/licenses/LICENSE-2.0
13 | %%
14 | %% Unless required by applicable law or agreed to in writing,
15 | %% software distributed under the License is distributed on an
16 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 | %% KIND, either express or implied. See the License for the
18 | %% specific language governing permissions and limitations
19 | %% under the License.
20 | %%
21 | %% -------------------------------------------------------------------
22 | -module(basho_bench_driver_null).
23 |
24 | -export([new/1,
25 | run/4]).
26 |
27 | -include("basho_bench.hrl").
28 |
29 | %% ====================================================================
30 | %% API
31 | %% ====================================================================
32 |
33 | new(_Id) ->
34 | {ok, undefined}.
35 |
36 | run(get, KeyGen, _ValueGen, _State) ->
37 | Key = KeyGen(),
38 | {ok, Key};
39 | run(put, KeyGen, ValueGen, _State) ->
40 | Key = KeyGen(),
41 | ValueGen(),
42 | {ok, Key};
43 | run(delete, KeyGen, _ValueGen, _State) ->
44 | Key = KeyGen(),
45 | {ok, Key}.
46 |
47 |
--------------------------------------------------------------------------------
/priv/compare.r:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env Rscript --vanilla
2 |
3 | source("priv/common.r")
4 |
5 | # Setup parameters for the script
6 | params = matrix(c(
7 | 'width', 'w', 2, "integer",
8 | 'height', 'h', 2, "integer",
9 | 'outfile', 'o', 2, "character",
10 | 'dir1', 'i', 1, "character",
11 | 'tag1', 'j', 1, "character",
12 | 'dir2', 'k', 1, "character",
13 | 'tag2', 'l', 1, "character"
14 | ), ncol=4, byrow=TRUE)
15 |
16 | # Parse the parameters
17 | opt = getopt(params)
18 |
19 | # Initialize defaults for opt
20 | if (is.null(opt$width)) { opt$width = 1024 }
21 | if (is.null(opt$height)) { opt$height = 768 }
22 | if (is.null(opt$outfile)) { opt$outfile = "compare.png" }
23 |
24 | # Load the benchmark data for each directory
25 | b1 = load_benchmark(opt$dir1)
26 | b2 = load_benchmark(opt$dir2)
27 |
28 | # If there is no actual data available, bail
29 | if (nrow(b1$latencies) == 0)
30 | {
31 | stop("No latency information available to analyze in ", opt$indir)
32 | }
33 |
34 | if (nrow(b2$latencies) == 0)
35 | {
36 | stop("No latency information available to analyze in ", opt$indir)
37 | }
38 |
39 | png(file = opt$outfile, width = opt$width, height = opt$height)
40 |
41 | # Tag the summary frames for each benchmark so that we can distinguish
42 | # between them in the legend.
43 | b1$summary$tag <- opt$tag1
44 | b2$summary$tag <- opt$tag2
45 |
46 | # Compare the req/sec between the two datasets
47 | plot1 <- qplot(elapsed, total / window,
48 | data = b1$summary,
49 | color = tag,
50 | geom = "smooth",
51 | xlab = "Elapsed Secs",
52 | ylab = "Req/sec",
53 | main = "Throughput") + geom_smooth(data = b2$summary)
54 |
55 |
56 | grid.newpage()
57 |
58 | pushViewport(viewport(layout = grid.layout(3, 1)))
59 |
60 | vplayout <- function(x,y) viewport(layout.pos.row = x, layout.pos.col = y)
61 |
62 | print(plot1, vp = vplayout(1,1))
63 | #print(plot2, vp = vplayout(2,1))
64 | #print(plot3, vp = vplayout(3,1))
65 |
66 | dev.off()
67 |
68 |
--------------------------------------------------------------------------------
/src/basho_bench_driver_dets.erl:
--------------------------------------------------------------------------------
1 | %% -------------------------------------------------------------------
2 | %%
3 | %% basho_bench: Benchmarking Suite
4 | %%
5 | %% Copyright (c) 2009-2010 Basho Techonologies
6 | %%
7 | %% This file is provided to you under the Apache License,
8 | %% Version 2.0 (the "License"); you may not use this file
9 | %% except in compliance with the License. You may obtain
10 | %% a copy of the License at
11 | %%
12 | %% http://www.apache.org/licenses/LICENSE-2.0
13 | %%
14 | %% Unless required by applicable law or agreed to in writing,
15 | %% software distributed under the License is distributed on an
16 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 | %% KIND, either express or implied. See the License for the
18 | %% specific language governing permissions and limitations
19 | %% under the License.
20 | %%
21 | %% -------------------------------------------------------------------
22 | -module(basho_bench_driver_dets).
23 |
24 | -export([new/1,
25 | run/4]).
26 |
27 | -include("basho_bench.hrl").
28 |
29 | %% ====================================================================
30 | %% API
31 | %% ====================================================================
32 |
33 | new(_Id) ->
34 | File = basho_bench_config:get(dets_file, ?MODULE),
35 | {ok, _} = dets:open_file(?MODULE, [{file, File},
36 | {min_no_slots, 8192},
37 | {max_no_slots, 16777216}]),
38 | {ok, undefined}.
39 |
40 | run(get, KeyGen, _ValueGen, State) ->
41 | Key = KeyGen(),
42 | case dets:lookup(?MODULE, Key) of
43 | [] ->
44 | {ok, State};
45 | [{Key, _}] ->
46 | {ok, State};
47 | {error, Reason} ->
48 | {error, Reason, State}
49 | end;
50 | run(put, KeyGen, ValueGen, State) ->
51 | ok = dets:insert(?MODULE, {KeyGen(), ValueGen()}),
52 | {ok, State};
53 | run(delete, KeyGen, _ValueGen, State) ->
54 | ok = dets:delete(?MODULE, KeyGen()),
55 | {ok, State}.
56 |
57 |
--------------------------------------------------------------------------------
/src/basho_bench_config.erl:
--------------------------------------------------------------------------------
1 | %% -------------------------------------------------------------------
2 | %%
3 | %% basho_bench: Benchmarking Suite
4 | %%
5 | %% Copyright (c) 2009-2010 Basho Techonologies
6 | %%
7 | %% This file is provided to you under the Apache License,
8 | %% Version 2.0 (the "License"); you may not use this file
9 | %% except in compliance with the License. You may obtain
10 | %% a copy of the License at
11 | %%
12 | %% http://www.apache.org/licenses/LICENSE-2.0
13 | %%
14 | %% Unless required by applicable law or agreed to in writing,
15 | %% software distributed under the License is distributed on an
16 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 | %% KIND, either express or implied. See the License for the
18 | %% specific language governing permissions and limitations
19 | %% under the License.
20 | %%
21 | %% -------------------------------------------------------------------
22 | -module(basho_bench_config).
23 |
24 | -export([load/1,
25 | set/2,
26 | get/1, get/2]).
27 |
28 | -include("basho_bench.hrl").
29 |
30 | %% ===================================================================
31 | %% Public API
32 | %% ===================================================================
33 |
34 | load(File) ->
35 | case file:consult(File) of
36 | {ok, Terms} ->
37 | load_config(Terms);
38 | {error, Reason} ->
39 | ?FAIL_MSG("Failed to parse config file ~s: ~p\n", [File, Reason])
40 | end.
41 |
42 | set(Key, Value) ->
43 | ok = application:set_env(basho_bench, Key, Value).
44 |
45 | get(Key) ->
46 | case application:get_env(basho_bench, Key) of
47 | {ok, Value} ->
48 | Value;
49 | undefined ->
50 | erlang:error("Missing configuration key", [Key])
51 | end.
52 |
53 | get(Key, Default) ->
54 | case application:get_env(basho_bench, Key) of
55 | {ok, Value} ->
56 | Value;
57 | _ ->
58 | Default
59 | end.
60 |
61 |
62 | %% ===================================================================
63 | %% Internal functions
64 | %% ===================================================================
65 |
66 | load_config([]) ->
67 | ok;
68 | load_config([{Key, Value} | Rest]) ->
69 | ?MODULE:set(Key, Value),
70 | load_config(Rest);
71 | load_config([ Other | Rest]) ->
72 | ?WARN("Ignoring non-tuple config value: ~p\n", [Other]),
73 | load_config(Rest).
74 |
--------------------------------------------------------------------------------
/src/basho_bench_app.erl:
--------------------------------------------------------------------------------
1 | %% -------------------------------------------------------------------
2 | %%
3 | %% basho_bench: Benchmarking Suite
4 | %%
5 | %% Copyright (c) 2009-2010 Basho Techonologies
6 | %%
7 | %% This file is provided to you under the Apache License,
8 | %% Version 2.0 (the "License"); you may not use this file
9 | %% except in compliance with the License. You may obtain
10 | %% a copy of the License at
11 | %%
12 | %% http://www.apache.org/licenses/LICENSE-2.0
13 | %%
14 | %% Unless required by applicable law or agreed to in writing,
15 | %% software distributed under the License is distributed on an
16 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 | %% KIND, either express or implied. See the License for the
18 | %% specific language governing permissions and limitations
19 | %% under the License.
20 | %%
21 | %% -------------------------------------------------------------------
22 | -module(basho_bench_app).
23 |
24 | -behaviour(application).
25 |
26 | %% API
27 | -export([start/0,
28 | stop/0,
29 | is_running/0]).
30 |
31 | %% Application callbacks
32 | -export([start/2, stop/1]).
33 |
34 |
35 | %% ===================================================================
36 | %% API
37 | %%===================================================================
38 |
39 | start() ->
40 | %% Redirect all SASL logging into a text file
41 | application:load(sasl),
42 | application:set_env(sasl, sasl_error_logger, {file, "log.sasl.txt"}),
43 | ok = application:start(sasl),
44 |
45 | %% Make sure crypto is available
46 | ok = application:start(crypto),
47 |
48 | %% Start up our application -- mark it as permanent so that the node
49 | %% will be killed if we go down
50 | application:start(basho_bench, permanent).
51 |
52 | stop() ->
53 | application:stop(basho_bench).
54 |
55 | is_running() ->
56 | application:get_env(basho_bench_app, is_running) == {ok, true}.
57 |
58 |
59 | %% ===================================================================
60 | %% Application callbacks
61 | %%===================================================================
62 |
63 | start(_StartType, _StartArgs) ->
64 | {ok, Pid} = basho_bench_sup:start_link(),
65 | application:set_env(basho_bench_app, is_running, true),
66 | ok = basho_bench_stats:run(),
67 | ok = basho_bench_worker:run(basho_bench_sup:workers()),
68 | {ok, Pid}.
69 |
70 |
71 | stop(_State) ->
72 | ok.
73 |
74 | %% ===================================================================
75 | %% Internal functions
76 | %% ===================================================================
77 |
--------------------------------------------------------------------------------
/ebin/basho_bench.app:
--------------------------------------------------------------------------------
1 | {application, basho_bench,
2 | [{description, "Riak Benchmarking Suite"},
3 | {vsn, "0.1"},
4 | {modules, [
5 | basho_bench,
6 | basho_bench_app,
7 | basho_bench_config,
8 | basho_bench_driver_dets,
9 | basho_bench_driver_http_raw,
10 | basho_bench_driver_innostore,
11 | basho_bench_driver_riakc_pb,
12 | basho_bench_driver_riakclient,
13 | basho_bench_driver_cassandra,
14 | basho_bench_driver_bitcask,
15 | basho_bench_driver_null,
16 | basho_bench_log,
17 | basho_bench_keygen,
18 | basho_bench_stats,
19 | basho_bench_sup,
20 | basho_bench_worker,
21 | basho_bench_valgen
22 | ]},
23 | {registered, [ basho_bench_sup ]},
24 | {applications, [kernel,
25 | stdlib,
26 | sasl]},
27 | {mod, {basho_bench_app, []}},
28 | {env, [
29 | %%
30 | %% Mode of load generation:
31 | %% max - Generate as many requests as possible per worker
32 | %% {rate, Rate} - Exp. distributed Mean reqs/sec
33 | %%
34 | {mode, {rate, 5}},
35 |
36 | %%
37 | %% Default log level
38 | %%
39 | {log_level, debug},
40 |
41 | %%
42 | %% Base test output directory
43 | %%
44 | {test_dir, "tests"},
45 |
46 | %%
47 | %% Test duration (minutes)
48 | %%
49 | {duration, 5},
50 |
51 | %%
52 | %% Number of concurrent workers
53 | %%
54 | {concurrent, 3},
55 |
56 | %%
57 | %% Driver module for the current test
58 | %%
59 | {driver, basho_bench_driver_http_raw},
60 |
61 | %%
62 | %% Operations (and associated mix). Note that
63 | %% the driver may not implement every operation.
64 | %%
65 | {operations, [{get, 4},
66 | {put, 4},
67 | {delete, 1}]},
68 |
69 | %%
70 | %% Interval on which to report latencies and status (seconds)
71 | %%
72 | {report_interval, 10},
73 |
74 | %%
75 | %% Key generators
76 | %%
77 | %% {uniform_int, N} - Choose a uniformly distributed integer between 0 and N
78 | %%
79 | {key_generator, {uniform_int, 100000}},
80 |
81 | %%
82 | %% Value generators
83 | %%
84 | %% {fixed_bin, N} - Fixed size binary blob of N bytes
85 | %%
86 | {value_generator, {fixed_bin, 100}},
87 |
88 | %%
89 | %% RNG Seed -- ensures consistent generation of key/value sizes (but not content!)
90 | %%
91 | {rng_seed, {42, 23, 12}}
92 | ]}
93 | ]}.
94 |
--------------------------------------------------------------------------------
/src/basho_bench_sup.erl:
--------------------------------------------------------------------------------
1 | %% -------------------------------------------------------------------
2 | %%
3 | %% basho_bench: Benchmarking Suite
4 | %%
5 | %% Copyright (c) 2009-2010 Basho Techonologies
6 | %%
7 | %% This file is provided to you under the Apache License,
8 | %% Version 2.0 (the "License"); you may not use this file
9 | %% except in compliance with the License. You may obtain
10 | %% a copy of the License at
11 | %%
12 | %% http://www.apache.org/licenses/LICENSE-2.0
13 | %%
14 | %% Unless required by applicable law or agreed to in writing,
15 | %% software distributed under the License is distributed on an
16 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 | %% KIND, either express or implied. See the License for the
18 | %% specific language governing permissions and limitations
19 | %% under the License.
20 | %%
21 | %% -------------------------------------------------------------------
22 | -module(basho_bench_sup).
23 |
24 | -behaviour(supervisor).
25 |
26 | %% API
27 | -export([start_link/0,
28 | workers/0,
29 | stop_child/1]).
30 |
31 | %% Supervisor callbacks
32 | -export([init/1]).
33 |
34 | -include("basho_bench.hrl").
35 |
36 | %% Helper macro for declaring children of supervisor
37 | -define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}).
38 |
39 | %% ===================================================================
40 | %% API functions
41 | %% ===================================================================
42 |
43 | start_link() ->
44 | supervisor:start_link({local, ?MODULE}, ?MODULE, []).
45 |
46 | workers() ->
47 | [Pid || {_Id, Pid, worker, [basho_bench_worker]} <- supervisor:which_children(?MODULE)].
48 |
49 | stop_child(Id) ->
50 | ok = supervisor:terminate_child(?MODULE, Id),
51 | ok = supervisor:delete_child(?MODULE, Id).
52 |
53 |
54 | %% ===================================================================
55 | %% Supervisor callbacks
56 | %% ===================================================================
57 |
58 | init([]) ->
59 | %% Get the number concurrent workers we're expecting and generate child
60 | %% specs for each
61 | Workers = worker_specs(basho_bench_config:get(concurrent), []),
62 | {ok, {{one_for_one, 5, 10}, [?CHILD(basho_bench_log, worker),
63 | ?CHILD(basho_bench_stats, worker)] ++ Workers}}.
64 |
65 |
66 | %% ===================================================================
67 | %% Internal functions
68 | %% ===================================================================
69 |
70 | worker_specs(0, Acc) ->
71 | Acc;
72 | worker_specs(Count, Acc) ->
73 | Id = list_to_atom(lists:concat(['basho_bench_worker_', Count])),
74 | Spec = {Id, {basho_bench_worker, start_link, [Id, Count]},
75 | permanent, 5000, worker, [basho_bench_worker]},
76 | worker_specs(Count-1, [Spec | Acc]).
77 |
--------------------------------------------------------------------------------
/priv/summary.r:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env Rscript --vanilla
2 |
3 | # Parse the --file= argument out of command line args and
4 | # determine where base directory is so that we can source
5 | # our common sub-routines
6 | arg0 <- sub("--file=(.*)", "\\1", grep("--file=", commandArgs(), value = TRUE))
7 | dir0 <- dirname(arg0)
8 | source(file.path(dir0, "common.r"))
9 |
10 | # Setup parameters for the script
11 | params = matrix(c(
12 | 'help', 'h', 0, "logical",
13 | 'width', 'x', 2, "integer",
14 | 'height', 'y', 2, "integer",
15 | 'outfile', 'o', 2, "character",
16 | 'indir', 'i', 2, "character"
17 | ), ncol=4, byrow=TRUE)
18 |
19 | # Parse the parameters
20 | opt = getopt(params)
21 |
22 | if (!is.null(opt$help))
23 | {
24 | cat(paste(getopt(params, command = basename(arg0), usage = TRUE)))
25 | q(status=1)
26 | }
27 |
28 | # Initialize defaults for opt
29 | if (is.null(opt$width)) { opt$width = 1024 }
30 | if (is.null(opt$height)) { opt$height = 768 }
31 | if (is.null(opt$indir)) { opt$indir = "current"}
32 | if (is.null(opt$outfile)) { opt$outfile = file.path(opt$indir, "summary.png") }
33 |
34 | # Load the benchmark data
35 | b = load_benchmark(opt$indir)
36 |
37 | # If there is no actual data available, bail
38 | if (nrow(b$latencies) == 0)
39 | {
40 | stop("No latency information available to analyze in ", opt$indir)
41 | }
42 |
43 | png(file = opt$outfile, width = opt$width, height = opt$height)
44 |
45 | # First plot req/sec from summary
46 | plot1 <- qplot(elapsed, total / window, data = b$summary,
47 | geom = "smooth",
48 | xlab = "Elapsed Secs", ylab = "Op/sec",
49 | main = "Throughput")
50 |
51 | # Setup common elements of the latency plots
52 | latency_plot <- ggplot(b$latencies, aes(x = elapsed)) +
53 | facet_grid(. ~ op) +
54 | labs(x = "Elapsed Secs", y = "Latency (ms)")
55 |
56 | # Plot 99 and 99.9th percentiles
57 | plot2 <- latency_plot +
58 | geom_smooth(aes(y = X99th, color = "X99th")) +
59 | geom_smooth(aes(y = X99_9th, color = "X99_9th")) +
60 | scale_color_hue("Percentile",
61 | breaks = c("X99th", "X99_9th"),
62 | labels = c("99th", "99.9th"))
63 |
64 |
65 | # Plot median, mean and 95th percentiles
66 | plot3 <- latency_plot +
67 | geom_smooth(aes(y = median, color = "median")) +
68 | geom_smooth(aes(y = mean, color = "mean")) +
69 | geom_smooth(aes(y = X95th, color = "X95th")) +
70 | scale_color_hue("Percentile",
71 | breaks = c("median", "mean", "X95th"),
72 | labels = c("Median", "Mean", "95th"))
73 |
74 | grid.newpage()
75 |
76 | pushViewport(viewport(layout = grid.layout(3, 1)))
77 |
78 | vplayout <- function(x,y) viewport(layout.pos.row = x, layout.pos.col = y)
79 |
80 | print(plot1, vp = vplayout(1,1))
81 | print(plot2, vp = vplayout(2,1))
82 | print(plot3, vp = vplayout(3,1))
83 |
84 | dev.off()
85 |
--------------------------------------------------------------------------------
/src/basho_bench_valgen.erl:
--------------------------------------------------------------------------------
1 | %% -------------------------------------------------------------------
2 | %%
3 | %% basho_bench: Benchmarking Suite
4 | %%
5 | %% Copyright (c) 2009-2010 Basho Techonologies
6 | %%
7 | %% This file is provided to you under the Apache License,
8 | %% Version 2.0 (the "License"); you may not use this file
9 | %% except in compliance with the License. You may obtain
10 | %% a copy of the License at
11 | %%
12 | %% http://www.apache.org/licenses/LICENSE-2.0
13 | %%
14 | %% Unless required by applicable law or agreed to in writing,
15 | %% software distributed under the License is distributed on an
16 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 | %% KIND, either express or implied. See the License for the
18 | %% specific language governing permissions and limitations
19 | %% under the License.
20 | %%
21 | %% -------------------------------------------------------------------
22 | -module(basho_bench_valgen).
23 |
24 | -export([new/2,
25 | dimension/2]).
26 |
27 | -include("basho_bench.hrl").
28 |
29 | %% ====================================================================
30 | %% API
31 | %% ====================================================================
32 |
33 | new({fixed_bin, Size}, _Id) ->
34 | Source = init_source(),
35 | fun() -> data_block(Source, Size) end;
36 | new({exponential_bin, MinSize, Mean}, _Id) ->
37 | Source = init_source(),
38 | fun() -> data_block(Source, MinSize + trunc(stats_rv:exponential(1 / Mean))) end;
39 | new({uniform_bin, MinSize, MaxSize}, _Id) ->
40 | Source = init_source(),
41 | Diff = MaxSize - MinSize,
42 | fun() -> data_block(Source, MinSize + random:uniform(Diff)) end;
43 | new({function, Module, Function, Args}, Id) ->
44 | case code:ensure_loaded(Module) of
45 | {module, Module} ->
46 | erlang:apply(Module, Function, [Id] ++ Args);
47 | _Error ->
48 | ?FAIL_MSG("Could not find valgen function: ~p:~p\n", [Module, Function])
49 | end;
50 | new(Other, _Id) ->
51 | ?FAIL_MSG("Unsupported value generator requested: ~p\n", [Other]).
52 |
53 | dimension({fixed_bin, Size}, KeyDimension) ->
54 | Size * KeyDimension;
55 | dimension(_Other, _) ->
56 | 0.0.
57 |
58 |
59 |
60 | %% ====================================================================
61 | %% Internal Functions
62 | %% ====================================================================
63 |
64 | init_source() ->
65 | SourceSz = basho_bench_config:get(value_generator_source_size, 1048576),
66 | {SourceSz, crypto:rand_bytes(SourceSz)}.
67 |
68 | data_block({SourceSz, Source}, BlockSize) ->
69 | case SourceSz - BlockSize > 0 of
70 | true ->
71 | Offset = random:uniform(SourceSz - BlockSize),
72 | <<_:Offset/bytes, Slice:BlockSize/bytes, _Rest/binary>> = Source,
73 | Slice;
74 | false ->
75 | ?WARN("value_generator_source_size is too small; it needs a value > ~p.\n",
76 | [BlockSize]),
77 | Source
78 | end.
79 |
--------------------------------------------------------------------------------
/src/basho_bench_driver_innostore.erl:
--------------------------------------------------------------------------------
1 | %% -------------------------------------------------------------------
2 | %%
3 | %% basho_bench: Benchmarking Suite
4 | %%
5 | %% Copyright (c) 2009-2010 Basho Techonologies
6 | %%
7 | %% This file is provided to you under the Apache License,
8 | %% Version 2.0 (the "License"); you may not use this file
9 | %% except in compliance with the License. You may obtain
10 | %% a copy of the License at
11 | %%
12 | %% http://www.apache.org/licenses/LICENSE-2.0
13 | %%
14 | %% Unless required by applicable law or agreed to in writing,
15 | %% software distributed under the License is distributed on an
16 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 | %% KIND, either express or implied. See the License for the
18 | %% specific language governing permissions and limitations
19 | %% under the License.
20 | %%
21 | %% -------------------------------------------------------------------
22 | -module(basho_bench_driver_innostore).
23 |
24 | -export([new/1,
25 | run/4]).
26 |
27 | -include("basho_bench.hrl").
28 |
29 | %% ====================================================================
30 | %% API
31 | %% ====================================================================
32 |
33 | new(_Id) ->
34 | %% Make sure innostore app is available
35 | case code:which(innostore) of
36 | non_existing ->
37 | ?FAIL_MSG("~s requires innostore to be available on code path.\n",
38 | [?MODULE]);
39 | _ ->
40 | ok
41 | end,
42 |
43 | %% Pull the innodb_config key which has all the key/value pairs for the innostore
44 | %% engine -- stuff everything into the innostore application namespace
45 | %% so that starting innostore will pull it in.
46 | application:load(innostore),
47 | InnoConfig = basho_bench_config:get(innostore_config, []),
48 | [ok = application:set_env(innostore, K, V) || {K, V} <- InnoConfig],
49 |
50 | Bucket = basho_bench_config:get(innostore_bucket, <<"test">>),
51 | {ok, Port} = innostore:connect(),
52 | case innostore:open_keystore(Bucket, Port) of
53 | {ok, Store} ->
54 | {ok, Store};
55 | {error, Reason} ->
56 | ?FAIL_MSG("Failed to open keystore ~p: ~p\n", [Bucket, Reason])
57 | end.
58 |
59 |
60 | run(get, KeyGen, _ValueGen, State) ->
61 | case innostore:get(KeyGen(), State) of
62 | {ok, not_found} ->
63 | {ok, State};
64 | {ok, _Value} ->
65 | {ok, State};
66 | {error, Reason} ->
67 | {error, Reason, State}
68 | end;
69 | run(put, KeyGen, ValueGen, State) ->
70 | case innostore:put(KeyGen(), ValueGen(), State) of
71 | ok ->
72 | {ok, State};
73 | {error, Reason} ->
74 | {error, State, Reason}
75 | end;
76 | run(delete, KeyGen, _ValueGen, State) ->
77 | case innostore:delete(KeyGen(), State) of
78 | ok ->
79 | {ok, State};
80 | {error, Reason} ->
81 | {error, Reason, State}
82 | end.
83 |
--------------------------------------------------------------------------------
/src/basho_bench_driver_cassandra.erl:
--------------------------------------------------------------------------------
1 | %% -------------------------------------------------------------------
2 | %%
3 | %% basho_bench: Benchmarking Suite
4 | %%
5 | %% Copyright (c) 2009-2010 Basho Techonologies
6 | %%
7 | %% This file is provided to you under the Apache License,
8 | %% Version 2.0 (the "License"); you may not use this file
9 | %% except in compliance with the License. You may obtain
10 | %% a copy of the License at
11 | %%
12 | %% http://www.apache.org/licenses/LICENSE-2.0
13 | %%
14 | %% Unless required by applicable law or agreed to in writing,
15 | %% software distributed under the License is distributed on an
16 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 | %% KIND, either express or implied. See the License for the
18 | %% specific language governing permissions and limitations
19 | %% under the License.
20 | %%
21 | %% -------------------------------------------------------------------
22 | -module(basho_bench_driver_cassandra).
23 |
24 | -export([new/1,
25 | run/4]).
26 |
27 | -include("basho_bench.hrl").
28 | -include_lib("casbench/include/cassandra_thrift.hrl").
29 |
30 | -record(state, { client,
31 | keyspace,
32 | colpath }).
33 |
34 |
35 | %% ====================================================================
36 | %% API
37 | %% ====================================================================
38 |
39 | new(Id) ->
40 | %% Make sure the path is setup such that we can get at riak_client
41 | case code:which(cassandra_thrift) of
42 | non_existing ->
43 | ?FAIL_MSG("~s requires cassandra_thrift module to be available on code path.\n",
44 | [?MODULE]);
45 | _ ->
46 | ok
47 | end,
48 |
49 | Hosts = basho_bench_config:get(cassandra_hosts, ["localhost"]),
50 | Port = basho_bench_config:get(cassandra_port, 9160),
51 | Keyspace = basho_bench_config:get(cassandra_keyspace, "Keyspace1"),
52 | ColPath = #columnPath { column_family = "Standard1", column = "col1" },
53 |
54 | %% Choose the node using our ID as a modulus
55 | TargetHost = lists:nth((Id rem length(Hosts)+1), Hosts),
56 | ?INFO("Using target ~s:~p for worker ~p\n", [TargetHost, Port, Id]),
57 |
58 | case thrift_client:start_link(TargetHost, Port, cassandra_thrift) of
59 | {ok, Client} ->
60 | {ok, #state { client = Client,
61 | keyspace = Keyspace,
62 | colpath = ColPath }};
63 | {error, Reason} ->
64 | ?FAIL_MSG("Failed to get a thrift_client for ~p: ~p\n", [TargetHost, Reason])
65 | end.
66 |
67 | call(State, Op, Args) ->
68 | (catch thrift_client:call(State#state.client, Op, Args)).
69 |
70 | tstamp() ->
71 | {Mega, Sec, _Micro} = now(),
72 | (Mega * 1000000) + Sec.
73 |
74 |
75 | run(get, KeyGen, _ValueGen, State) ->
76 | Key = KeyGen(),
77 | case call(State, get, [State#state.keyspace, Key, State#state.colpath, 1]) of
78 | {ok, _} ->
79 | {ok, State};
80 | {notFoundException} ->
81 | {ok, State};
82 | {'EXIT', {timeout, _}} ->
83 | {error, timeout, State};
84 | Error ->
85 | {error, Error, State}
86 | end;
87 | run(put, KeyGen, ValueGen, State) ->
88 | case call(State, insert, [State#state.keyspace, KeyGen(), State#state.colpath,
89 | ValueGen(), tstamp(), 1]) of
90 | {ok, ok} ->
91 | {ok, State};
92 | {'EXIT', {timeout, _}} ->
93 | {error, timeout, State};
94 | Error ->
95 | {error, Error, State}
96 | end.
97 |
--------------------------------------------------------------------------------
/src/basho_bench_driver_bitcask.erl:
--------------------------------------------------------------------------------
1 | %% -------------------------------------------------------------------
2 | %%
3 | %% basho_bench: Benchmarking Suite
4 | %%
5 | %% Copyright (c) 2009-2010 Basho Techonologies
6 | %%
7 | %% This file is provided to you under the Apache License,
8 | %% Version 2.0 (the "License"); you may not use this file
9 | %% except in compliance with the License. You may obtain
10 | %% a copy of the License at
11 | %%
12 | %% http://www.apache.org/licenses/LICENSE-2.0
13 | %%
14 | %% Unless required by applicable law or agreed to in writing,
15 | %% software distributed under the License is distributed on an
16 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 | %% KIND, either express or implied. See the License for the
18 | %% specific language governing permissions and limitations
19 | %% under the License.
20 | %%
21 | %% -------------------------------------------------------------------
22 | -module(basho_bench_driver_bitcask).
23 |
24 | -export([new/1,
25 | run/4]).
26 |
27 | -include("basho_bench.hrl").
28 |
29 | -record(state, { file,
30 | sync_interval,
31 | last_sync }).
32 |
33 | %% ====================================================================
34 | %% API
35 | %% ====================================================================
36 |
37 | new(_Id) ->
38 | %% Make sure bitcask is available
39 | case code:which(bitcask) of
40 | non_existing ->
41 | ?FAIL_MSG("~s requires bitcask to be available on code path.\n",
42 | [?MODULE]);
43 | _ ->
44 | ok
45 | end,
46 |
47 | %% Get the target directory
48 | Dir = basho_bench_config:get(bitcask_dir, "."),
49 | Filename = filename:join(Dir, "test.bitcask"),
50 |
51 | %% Look for sync interval config
52 | case basho_bench_config:get(bitcask_sync_interval, infinity) of
53 | Value when is_integer(Value) ->
54 | SyncInterval = Value;
55 | infinity ->
56 | SyncInterval = infinity
57 | end,
58 |
59 | %% Get any bitcask flags
60 | Flags = basho_bench_config:get(bitcask_flags, []),
61 | case bitcask:open(Filename, [read_write] ++ Flags) of
62 | {error, Reason} ->
63 | ?FAIL_MSG("Failed to open bitcask in ~s: ~p\n", [Filename, Reason]);
64 | File ->
65 | {ok, #state { file = File, sync_interval = SyncInterval,
66 | last_sync = os:timestamp() }}
67 | end.
68 |
69 |
70 |
71 | run(get, KeyGen, _ValueGen, State) ->
72 | State1 = maybe_sync(State),
73 | case bitcask:get(State1#state.file, KeyGen()) of
74 | {ok, _Value} ->
75 | {ok, State1};
76 | not_found ->
77 | {ok, State1};
78 | {error, Reason} ->
79 | {error, Reason}
80 | end;
81 | run(put, KeyGen, ValueGen, State) ->
82 | State1 = maybe_sync(State),
83 | case bitcask:put(State1#state.file, KeyGen(), ValueGen()) of
84 | ok ->
85 | {ok, State1};
86 | {error, Reason} ->
87 | {error, Reason}
88 | end.
89 |
90 |
91 |
92 | maybe_sync(#state { sync_interval = infinity } = State) ->
93 | State;
94 | maybe_sync(#state { sync_interval = SyncInterval } = State) ->
95 | Now = os:timestamp(),
96 | case timer:now_diff(Now, State#state.last_sync) / 1000000 of
97 | Value when Value >= SyncInterval ->
98 | bitcask:sync(State#state.file),
99 | State#state { last_sync = Now };
100 | _ ->
101 | State
102 | end.
103 |
--------------------------------------------------------------------------------
/src/basho_bench_log.erl:
--------------------------------------------------------------------------------
1 | %% -------------------------------------------------------------------
2 | %%
3 | %% basho_bench: Benchmarking Suite
4 | %%
5 | %% Copyright (c) 2009-2010 Basho Techonologies
6 | %%
7 | %% This file is provided to you under the Apache License,
8 | %% Version 2.0 (the "License"); you may not use this file
9 | %% except in compliance with the License. You may obtain
10 | %% a copy of the License at
11 | %%
12 | %% http://www.apache.org/licenses/LICENSE-2.0
13 | %%
14 | %% Unless required by applicable law or agreed to in writing,
15 | %% software distributed under the License is distributed on an
16 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 | %% KIND, either express or implied. See the License for the
18 | %% specific language governing permissions and limitations
19 | %% under the License.
20 | %%
21 | %% -------------------------------------------------------------------
22 | -module(basho_bench_log).
23 |
24 | -behaviour(gen_server).
25 |
26 | %% API
27 | -export([start_link/0,
28 | log/3]).
29 |
30 | %% gen_server callbacks
31 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
32 | terminate/2, code_change/3]).
33 |
34 | -record(state, { log_level,
35 | log_file }).
36 |
37 | %% ====================================================================
38 | %% API
39 | %% ====================================================================
40 |
41 | start_link() ->
42 | gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
43 |
44 | log(Level, Str, Args) ->
45 | case whereis(?MODULE) of
46 | undefined ->
47 | basic_log(Level, Str, Args);
48 | Pid ->
49 | gen_server:call(Pid, {log, Level, Str, Args})
50 | end.
51 |
52 |
53 | %% ====================================================================
54 | %% gen_server callbacks
55 | %% ====================================================================
56 |
57 | init([]) ->
58 | LogLevel = basho_bench_config:get(log_level),
59 | {ok, LogFile} = file:open("log.txt", [raw, binary, write]),
60 | {ok, #state{ log_level = LogLevel,
61 | log_file = LogFile }}.
62 |
63 | handle_call({log, Level, Str, Args}, _From, State) ->
64 | case should_log(State#state.log_level, Level) of
65 | true ->
66 | Message = io_lib:format(log_prefix(Level) ++ Str, Args),
67 | ok = file:write(State#state.log_file, Message),
68 | ok = io:format(Message);
69 | false ->
70 | ok
71 | end,
72 | {reply, ok, State}.
73 |
74 | handle_cast(_Msg, State) ->
75 | {noreply, State}.
76 |
77 | handle_info(_Info, State) ->
78 | {noreply, State}.
79 |
80 | terminate(_Reason, _State) ->
81 | ok.
82 |
83 | code_change(_OldVsn, State, _Extra) ->
84 | {ok, State}.
85 |
86 |
87 | %% ===================================================================
88 | %% Internal functions
89 | %% ===================================================================
90 |
91 | basic_log(Level, Str, Args) ->
92 | {ok, LogLevel} = application:get_env(basho_bench, log_level),
93 | case should_log(LogLevel, Level) of
94 | true ->
95 | io:format(log_prefix(Level) ++ Str, Args);
96 | false ->
97 | ok
98 | end.
99 |
100 | should_log(_, console) -> true;
101 | should_log(debug, _) -> true;
102 | should_log(info, debug) -> false;
103 | should_log(info, _) -> true;
104 | should_log(warn, debug) -> false;
105 | should_log(warn, info) -> false;
106 | should_log(warn, _) -> true;
107 | should_log(error, error) -> true;
108 | should_log(error, _) -> false;
109 | should_log(_, _) -> false.
110 |
111 | log_prefix(console) -> "";
112 | log_prefix(debug) -> "DEBUG:" ;
113 | log_prefix(info) -> "INFO: ";
114 | log_prefix(warn) -> "WARN: ";
115 | log_prefix(error) -> "ERROR: ".
116 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/src/basho_bench_keygen.erl:
--------------------------------------------------------------------------------
1 | %% -------------------------------------------------------------------
2 | %%
3 | %% basho_bench: Benchmarking Suite
4 | %%
5 | %% Copyright (c) 2009-2010 Basho Techonologies
6 | %%
7 | %% This file is provided to you under the Apache License,
8 | %% Version 2.0 (the "License"); you may not use this file
9 | %% except in compliance with the License. You may obtain
10 | %% a copy of the License at
11 | %%
12 | %% http://www.apache.org/licenses/LICENSE-2.0
13 | %%
14 | %% Unless required by applicable law or agreed to in writing,
15 | %% software distributed under the License is distributed on an
16 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 | %% KIND, either express or implied. See the License for the
18 | %% specific language governing permissions and limitations
19 | %% under the License.
20 | %%
21 | %% -------------------------------------------------------------------
22 | -module(basho_bench_keygen).
23 |
24 | -export([new/2,
25 | dimension/1]).
26 |
27 | -include("basho_bench.hrl").
28 |
29 | %% Use a fixed shape for Pareto that will yield the desired 80/20
30 | %% ratio of generated values.
31 | -define(PARETO_SHAPE, 1.5).
32 |
33 | %% ====================================================================
34 | %% API
35 | %% ====================================================================
36 |
37 | new({sequential_int, MaxKey}, _Id) ->
38 | Ref = make_ref(),
39 | fun() -> sequential_int_generator(Ref, MaxKey) end;
40 | new({sequential_int_bin, MaxKey}, _Id) ->
41 | Ref = make_ref(),
42 | fun() -> Key = sequential_int_generator(Ref, MaxKey), <> end;
43 | new({sequential_int_str, MaxKey}, _Id) ->
44 | Ref = make_ref(),
45 | fun() -> Key = sequential_int_generator(Ref, MaxKey), integer_to_list(Key) end;
46 | new({uniform_int_bin, MaxKey}, _Id) ->
47 | fun() -> Key = random:uniform(MaxKey), <> end;
48 | new({uniform_int_str, MaxKey}, _Id) ->
49 | fun() -> Key = random:uniform(MaxKey), integer_to_list(Key) end;
50 | new({uniform_int, MaxKey}, _Id) ->
51 | fun() -> random:uniform(MaxKey) end;
52 | new({pareto_int, MaxKey}, _Id) ->
53 | pareto(trunc(MaxKey * 0.2), ?PARETO_SHAPE);
54 | new({pareto_int_bin, MaxKey}, _Id) ->
55 | Pareto = pareto(trunc(MaxKey * 0.2), ?PARETO_SHAPE),
56 | fun() -> <<(Pareto()):32/native>> end;
57 | new({function, Module, Function, Args}, Id) ->
58 | case code:ensure_loaded(Module) of
59 | {module, Module} ->
60 | erlang:apply(Module, Function, [Id] ++ Args);
61 | _Error ->
62 | ?FAIL_MSG("Could not find keygen function: ~p:~p\n", [Module, Function])
63 | end;
64 | new(Other, _Id) ->
65 | ?FAIL_MSG("Unsupported key generator requested: ~p\n", [Other]).
66 |
67 |
68 | dimension({sequential_int, MaxKey}) ->
69 | MaxKey;
70 | dimension({sequential_int_bin, MaxKey}) ->
71 | MaxKey;
72 | dimension({sequential_int_str, MaxKey}) ->
73 | MaxKey;
74 | dimension({uniform_int_bin, MaxKey}) ->
75 | MaxKey;
76 | dimension({uniform_int_str, MaxKey}) ->
77 | MaxKey;
78 | dimension({uniform_int, MaxKey}) ->
79 | MaxKey;
80 | dimension(Other) ->
81 | ?INFO("No dimension available for key generator: ~p\n", [Other]),
82 | undefined.
83 |
84 |
85 |
86 |
87 | %% ====================================================================
88 | %% Internal functions
89 | %% ====================================================================
90 |
91 | pareto(Mean, Shape) ->
92 | S1 = (-1 / Shape),
93 | S2 = Mean * (Shape - 1),
94 | fun() ->
95 | U = 1 - random:uniform(),
96 | trunc((math:pow(U, S1) - 1) * S2)
97 | end.
98 |
99 |
100 | sequential_int_generator(Ref, MaxValue) ->
101 | %% A bit of evil here. We want to generate numbers in sequence and stop
102 | %% at MaxKey. This means we need state in our anonymous function. Use the process
103 | %% dictionary to keep track of where we are.
104 | case erlang:get({sigen, Ref}) of
105 | undefined ->
106 | erlang:put({sigen, Ref}, 1),
107 | 0;
108 | MaxValue ->
109 | throw({stop, empty_keygen});
110 | Value ->
111 | case Value rem 5000 of
112 | 0 ->
113 | ?DEBUG("sequential_int_gen: ~p (~w%)\n", [Value, trunc(100 * (Value / MaxValue))]);
114 | _ ->
115 | ok
116 | end,
117 | erlang:put({sigen, Ref}, Value+1),
118 | Value
119 | end.
120 |
--------------------------------------------------------------------------------
/src/basho_bench_driver_riakclient.erl:
--------------------------------------------------------------------------------
1 | %% -------------------------------------------------------------------
2 | %%
3 | %% basho_bench: Benchmarking Suite
4 | %%
5 | %% Copyright (c) 2009-2010 Basho Techonologies
6 | %%
7 | %% This file is provided to you under the Apache License,
8 | %% Version 2.0 (the "License"); you may not use this file
9 | %% except in compliance with the License. You may obtain
10 | %% a copy of the License at
11 | %%
12 | %% http://www.apache.org/licenses/LICENSE-2.0
13 | %%
14 | %% Unless required by applicable law or agreed to in writing,
15 | %% software distributed under the License is distributed on an
16 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 | %% KIND, either express or implied. See the License for the
18 | %% specific language governing permissions and limitations
19 | %% under the License.
20 | %%
21 | %% -------------------------------------------------------------------
22 | -module(basho_bench_driver_riakclient).
23 |
24 | -export([new/1,
25 | run/4]).
26 |
27 | -include("basho_bench.hrl").
28 |
29 | -record(state, { client,
30 | bucket,
31 | replies }).
32 |
33 | %% ====================================================================
34 | %% API
35 | %% ====================================================================
36 |
37 | new(Id) ->
38 | %% Make sure the path is setup such that we can get at riak_client
39 | case code:which(riak_client) of
40 | non_existing ->
41 | ?FAIL_MSG("~s requires riak_client module to be available on code path.\n",
42 | [?MODULE]);
43 | _ ->
44 | ok
45 | end,
46 |
47 | Nodes = basho_bench_config:get(riakclient_nodes),
48 | Cookie = basho_bench_config:get(riakclient_cookie, 'riak'),
49 | MyNode = basho_bench_config:get(riakclient_mynode, [basho_bench, longnames]),
50 | Replies = basho_bench_config:get(riakclient_replies, 2),
51 | Bucket = basho_bench_config:get(riakclient_bucket, <<"test">>),
52 |
53 | %% Try to spin up net_kernel
54 | case net_kernel:start(MyNode) of
55 | {ok, _} ->
56 | ?INFO("Net kernel started as ~p\n", [node()]);
57 | {error, {already_started, _}} ->
58 | ok;
59 | {error, Reason} ->
60 | ?FAIL_MSG("Failed to start net_kernel for ~p: ~p\n", [?MODULE, Reason])
61 | end,
62 |
63 | %% Initialize cookie for each of the nodes
64 | [true = erlang:set_cookie(N, Cookie) || N <- Nodes],
65 |
66 | %% Try to ping each of the nodes
67 | ping_each(Nodes),
68 |
69 | %% Choose the node using our ID as a modulus
70 | TargetNode = lists:nth((Id rem length(Nodes)+1), Nodes),
71 | ?INFO("Using target node ~p for worker ~p\n", [TargetNode, Id]),
72 |
73 | case riak:client_connect(TargetNode) of
74 | {ok, Client} ->
75 | {ok, #state { client = Client,
76 | bucket = Bucket,
77 | replies = Replies }};
78 | {error, Reason2} ->
79 | ?FAIL_MSG("Failed get a riak:client_connect to ~p: ~p\n", [TargetNode, Reason2])
80 | end.
81 |
82 | run(get, KeyGen, _ValueGen, State) ->
83 | Key = KeyGen(),
84 | case (State#state.client):get(State#state.bucket, Key, State#state.replies) of
85 | {ok, _} ->
86 | {ok, State};
87 | {error, notfound} ->
88 | {ok, State};
89 | {error, Reason} ->
90 | {error, Reason, State}
91 | end;
92 | run(put, KeyGen, ValueGen, State) ->
93 | Robj = riak_object:new(State#state.bucket, KeyGen(), ValueGen()),
94 | case (State#state.client):put(Robj, State#state.replies) of
95 | ok ->
96 | {ok, State};
97 | {error, Reason} ->
98 | {error, Reason, State}
99 | end;
100 | run(update, KeyGen, ValueGen, State) ->
101 | Key = KeyGen(),
102 | case (State#state.client):get(State#state.bucket, Key, State#state.replies) of
103 | {ok, Robj} ->
104 | Robj2 = riak_object:update_value(Robj, ValueGen()),
105 | case (State#state.client):put(Robj2, State#state.replies) of
106 | ok ->
107 | {ok, State};
108 | {error, Reason} ->
109 | {error, Reason, State}
110 | end;
111 | {error, notfound} ->
112 | Robj = riak_object:new(State#state.bucket, Key, ValueGen()),
113 | case (State#state.client):put(Robj, State#state.replies) of
114 | ok ->
115 | {ok, State};
116 | {error, Reason} ->
117 | {error, Reason, State}
118 | end
119 | end;
120 | run(delete, KeyGen, _ValueGen, State) ->
121 | case (State#state.client):delete(State#state.bucket, KeyGen(), State#state.replies) of
122 | ok ->
123 | {ok, State};
124 | {error, notfound} ->
125 | {ok, State};
126 | {error, Reason} ->
127 | {error, Reason, State}
128 | end.
129 |
130 |
131 | %% ====================================================================
132 | %% Internal functions
133 | %% ====================================================================
134 |
135 | ping_each([]) ->
136 | ok;
137 | ping_each([Node | Rest]) ->
138 | case net_adm:ping(Node) of
139 | pong ->
140 | ping_each(Rest);
141 | pang ->
142 | ?FAIL_MSG("Failed to ping node ~p\n", [Node])
143 | end.
144 |
--------------------------------------------------------------------------------
/src/basho_bench_driver_riakc_pb.erl:
--------------------------------------------------------------------------------
1 | %% -------------------------------------------------------------------
2 | %%
3 | %% basho_bench_driver_riakc_pb: Driver for riak protocol buffers client
4 | %%
5 | %% Copyright (c) 2009 Basho Techonologies
6 | %%
7 | %% This file is provided to you under the Apache License,
8 | %% Version 2.0 (the "License"); you may not use this file
9 | %% except in compliance with the License. You may obtain
10 | %% a copy of the License at
11 | %%
12 | %% http://www.apache.org/licenses/LICENSE-2.0
13 | %%
14 | %% Unless required by applicable law or agreed to in writing,
15 | %% software distributed under the License is distributed on an
16 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 | %% KIND, either express or implied. See the License for the
18 | %% specific language governing permissions and limitations
19 | %% under the License.
20 | %%
21 | %% -------------------------------------------------------------------
22 | -module(basho_bench_driver_riakc_pb).
23 |
24 | -export([new/1,
25 | run/4]).
26 |
27 | -include("basho_bench.hrl").
28 |
29 | -record(state, { pid,
30 | bucket,
31 | r,
32 | w,
33 | dw,
34 | rw}).
35 |
36 | %% ====================================================================
37 | %% API
38 | %% ====================================================================
39 |
40 | new(Id) ->
41 | %% Make sure the path is setup such that we can get at riak_client
42 | case code:which(riakc_pb_socket) of
43 | non_existing ->
44 | ?FAIL_MSG("~s requires riakc_pb_socket module to be available on code path.\n",
45 | [?MODULE]);
46 | _ ->
47 | ok
48 | end,
49 |
50 | Ips = basho_bench_config:get(riakc_pb_ips, [{127,0,0,1}]),
51 | Port = basho_bench_config:get(riakc_pb_port, 8087),
52 | %% riakc_pb_replies sets defaults for R, W, DW and RW.
53 | %% Each can be overridden separately
54 | Replies = basho_bench_config:get(riakc_pb_replies, 2),
55 | R = basho_bench_config:get(riakc_pb_r, Replies),
56 | W = basho_bench_config:get(riakc_pb_w, Replies),
57 | DW = basho_bench_config:get(riakc_pb_dw, Replies),
58 | RW = basho_bench_config:get(riakc_pb_rw, Replies),
59 | Bucket = basho_bench_config:get(riakc_pb_bucket, <<"test">>),
60 |
61 | %% Choose the node using our ID as a modulus
62 | TargetIp = lists:nth((Id rem length(Ips)+1), Ips),
63 | ?INFO("Using target ip ~p for worker ~p\n", [TargetIp, Id]),
64 |
65 | case riakc_pb_socket:start_link(TargetIp, Port) of
66 | {ok, Pid} ->
67 | {ok, #state { pid = Pid,
68 | bucket = Bucket,
69 | r = R,
70 | w = W,
71 | dw = DW,
72 | rw = RW
73 | }};
74 | {error, Reason2} ->
75 | ?FAIL_MSG("Failed to connect riakc_pb_socket to ~p port ~p: ~p\n",
76 | [TargetIp, Port, Reason2])
77 | end.
78 |
79 | run(get, KeyGen, _ValueGen, State) ->
80 | Key = KeyGen(),
81 | case riakc_pb_socket:get(State#state.pid, State#state.bucket, Key,
82 | [{r, State#state.r}]) of
83 | {ok, _} ->
84 | {ok, State};
85 | {error, notfound} ->
86 | {ok, State};
87 | {error, Reason} ->
88 | {error, Reason, State}
89 | end;
90 | run(put, KeyGen, ValueGen, State) ->
91 | Robj0 = riakc_obj:new(State#state.bucket, KeyGen()),
92 | Robj = riakc_obj:update_value(Robj0, ValueGen()),
93 | case riakc_pb_socket:put(State#state.pid, Robj, [{w, State#state.w},
94 | {dw, State#state.dw}]) of
95 | ok ->
96 | {ok, State};
97 | {error, Reason} ->
98 | {error, Reason, State}
99 | end;
100 | run(update, KeyGen, ValueGen, State) ->
101 | Key = KeyGen(),
102 | case riakc_pb_socket:get(State#state.pid, State#state.bucket,
103 | Key, [{r, State#state.r}]) of
104 | {ok, Robj} ->
105 | Robj2 = riakc_obj:update_value(Robj, ValueGen()),
106 | case riakc_pb_socket:put(State#state.pid, Robj2, [{w, State#state.w},
107 | {dw, State#state.dw}]) of
108 | ok ->
109 | {ok, State};
110 | {error, Reason} ->
111 | {error, Reason, State}
112 | end;
113 | {error, notfound} ->
114 | Robj0 = riakc_obj:new(State#state.bucket, KeyGen()),
115 | Robj = riakc_obj:update_value(Robj0, ValueGen()),
116 | case riakc_pb_socket:put(State#state.pid, Robj, [{w, State#state.w},
117 | {dw, State#state.dw}]) of
118 | ok ->
119 | {ok, State};
120 | {error, Reason} ->
121 | {error, Reason, State}
122 | end
123 | end;
124 | run(delete, KeyGen, _ValueGen, State) ->
125 | %% Pass on rw
126 | case riakc_pb_socket:delete(State#state.pid, State#state.bucket, KeyGen(),
127 | [{rw, State#state.rw}]) of
128 | ok ->
129 | {ok, State};
130 | {error, notfound} ->
131 | {ok, State};
132 | {error, Reason} ->
133 | {error, Reason, State}
134 | end.
135 |
136 |
137 | %% ====================================================================
138 | %% Internal functions
139 | %% ====================================================================
140 |
141 |
--------------------------------------------------------------------------------
/src/basho_bench.erl:
--------------------------------------------------------------------------------
1 | %% -------------------------------------------------------------------
2 | %%
3 | %% basho_bench: Benchmarking Suite
4 | %%
5 | %% Copyright (c) 2009-2010 Basho Techonologies
6 | %%
7 | %% This file is provided to you under the Apache License,
8 | %% Version 2.0 (the "License"); you may not use this file
9 | %% except in compliance with the License. You may obtain
10 | %% a copy of the License at
11 | %%
12 | %% http://www.apache.org/licenses/LICENSE-2.0
13 | %%
14 | %% Unless required by applicable law or agreed to in writing,
15 | %% software distributed under the License is distributed on an
16 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 | %% KIND, either express or implied. See the License for the
18 | %% specific language governing permissions and limitations
19 | %% under the License.
20 | %%
21 | %% -------------------------------------------------------------------
22 | -module(basho_bench).
23 |
24 | -export([main/1]).
25 |
26 | -include("basho_bench.hrl").
27 |
28 | %% ====================================================================
29 | %% API
30 | %% ====================================================================
31 |
32 | main([]) ->
33 | io:format("Usage: basho_bench CONFIG_FILE~n");
34 |
35 | main([Config]) ->
36 | %% Load baseline config
37 | ok = application:load(basho_bench),
38 |
39 | %% Load the config file
40 | basho_bench_config:load(Config),
41 |
42 | %% Init code path
43 | add_code_paths(basho_bench_config:get(code_paths, [])),
44 |
45 | %% If a source directory is specified, compile and load all .erl files found
46 | %% there.
47 | case basho_bench_config:get(source_dir, []) of
48 | [] ->
49 | ok;
50 | SourceDir ->
51 | load_source_files(SourceDir)
52 | end,
53 |
54 | %% Setup working directory for this test. All logs, stats, and config
55 | %% info will be placed here
56 | {ok, Cwd} = file:get_cwd(),
57 | TestId = id(),
58 | TestDir = filename:join([Cwd, basho_bench_config:get(test_dir), TestId]),
59 | ok = filelib:ensure_dir(filename:join(TestDir, "foobar")),
60 | basho_bench_config:set(test_id, TestId),
61 |
62 | %% Create a link to the test dir for convenience
63 | TestLink = filename:join([Cwd, basho_bench_config:get(test_dir), "current"]),
64 | [] = os:cmd(?FMT("rm -f ~s; ln -sf ~s ~s", [TestLink, TestDir, TestLink])),
65 |
66 | %% Copy the config into the test dir for posterity
67 | {ok, _} = file:copy(Config, filename:join(TestDir, filename:basename(Config))),
68 |
69 | %% Set our CWD to the test dir
70 | ok = file:set_cwd(TestDir),
71 |
72 | log_dimensions(),
73 |
74 | %% Spin up the application
75 | ok = basho_bench_app:start(),
76 |
77 | %% Pull the runtime duration from the config and sleep until that's passed OR
78 | %% the supervisor process exits
79 | Mref = erlang:monitor(process, whereis(basho_bench_sup)),
80 | DurationMins = basho_bench_config:get(duration),
81 | wait_for_stop(Mref, DurationMins).
82 |
83 |
84 |
85 |
86 |
87 | %% ====================================================================
88 | %% Internal functions
89 | %% ====================================================================
90 |
91 | wait_for_stop(Mref, infinity) ->
92 | receive
93 | {'DOWN', Mref, _, _, Info} ->
94 | ?CONSOLE("Test stopped: ~p\n", [Info])
95 | end;
96 | wait_for_stop(Mref, DurationMins) ->
97 | Duration = timer:minutes(DurationMins) + timer:seconds(1),
98 | receive
99 | {'DOWN', Mref, _, _, Info} ->
100 | ?CONSOLE("Test stopped: ~p\n", [Info])
101 |
102 | after Duration ->
103 | basho_bench_app:stop(),
104 | ?CONSOLE("Test completed after ~p mins.\n", [DurationMins])
105 | end.
106 |
107 |
108 |
109 | %%
110 | %% Construct a string suitable for use as a unique ID for this test run
111 | %%
112 | id() ->
113 | {{Y, M, D}, {H, Min, S}} = calendar:local_time(),
114 | ?FMT("~w~2..0w~2..0w_~2..0w~2..0w~2..0w", [Y, M, D, H, Min, S]).
115 |
116 |
117 | add_code_paths([]) ->
118 | ok;
119 | add_code_paths([Path | Rest]) ->
120 | Absname = filename:absname(Path),
121 | case filename:basename(Absname) of
122 | "ebin" ->
123 | true = code:add_path(Absname);
124 | _ ->
125 | true = code:add_path(filename:join(Absname, "ebin"))
126 | end,
127 | add_code_paths(Rest).
128 |
129 |
130 | %%
131 | %% Convert a number of bytes into a more user-friendly representation
132 | %%
133 | user_friendly_bytes(Size) ->
134 | lists:foldl(fun(Desc, {Sz, SzDesc}) ->
135 | case Sz > 1000 of
136 | true ->
137 | {Sz / 1024, Desc};
138 | false ->
139 | {Sz, SzDesc}
140 | end
141 | end,
142 | {Size, bytes}, ['KB', 'MB', 'GB']).
143 |
144 | log_dimensions() ->
145 | case basho_bench_keygen:dimension(basho_bench_config:get(key_generator)) of
146 | undefined ->
147 | ok;
148 | Keyspace ->
149 | Valspace = basho_bench_valgen:dimension(basho_bench_config:get(value_generator), Keyspace),
150 | {Size, Desc} = user_friendly_bytes(Valspace),
151 | ?INFO("Est. data size: ~.2f ~s\n", [Size, Desc])
152 | end.
153 |
154 |
155 | load_source_files(Dir) ->
156 | CompileFn = fun(F, _Acc) ->
157 | case compile:file(F, [report, binary]) of
158 | {ok, Mod, Bin} ->
159 | {module, Mod} = code:load_binary(Mod, F, Bin),
160 | ?INFO("Loaded ~p (~s)\n", [Mod, F]),
161 | ok;
162 | Error ->
163 | io:format("Failed to compile ~s: ~p\n", [F, Error])
164 | end
165 | end,
166 | filelib:fold_files(Dir, ".*.erl", false, CompileFn, ok).
167 |
--------------------------------------------------------------------------------
/src/basho_bench_stats.erl:
--------------------------------------------------------------------------------
1 | %% -------------------------------------------------------------------
2 | %%
3 | %% basho_bench: Benchmarking Suite
4 | %%
5 | %% Copyright (c) 2009-2010 Basho Techonologies
6 | %%
7 | %% This file is provided to you under the Apache License,
8 | %% Version 2.0 (the "License"); you may not use this file
9 | %% except in compliance with the License. You may obtain
10 | %% a copy of the License at
11 | %%
12 | %% http://www.apache.org/licenses/LICENSE-2.0
13 | %%
14 | %% Unless required by applicable law or agreed to in writing,
15 | %% software distributed under the License is distributed on an
16 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 | %% KIND, either express or implied. See the License for the
18 | %% specific language governing permissions and limitations
19 | %% under the License.
20 | %%
21 | %% -------------------------------------------------------------------
22 | -module(basho_bench_stats).
23 |
24 | -behaviour(gen_server).
25 |
26 | %% API
27 | -export([start_link/0,
28 | run/0,
29 | op_complete/3]).
30 |
31 | %% gen_server callbacks
32 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
33 | terminate/2, code_change/3]).
34 |
35 | -include("basho_bench.hrl").
36 |
37 | -record(state, { ops,
38 | start_time,
39 | last_write_time,
40 | report_interval,
41 | errors_since_last_report = false,
42 | summary_file}).
43 |
44 | %% Tracks latencies up to 5 secs w/ 250 us resolution
45 | -define(NEW_HIST, stats_histogram:new(0, 5000000, 20000)).
46 |
47 | %% ====================================================================
48 | %% API
49 | %% ====================================================================
50 |
51 | start_link() ->
52 | gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
53 |
54 | run() ->
55 | gen_server:call(?MODULE, run).
56 |
57 | op_complete(Op, Result, ElapsedUs) ->
58 | gen_server:call(?MODULE, {op, Op, Result, ElapsedUs}).
59 |
60 | %% ====================================================================
61 | %% gen_server callbacks
62 | %% ====================================================================
63 |
64 | init([]) ->
65 | %% Trap exits so we have a chance to flush data
66 | process_flag(trap_exit, true),
67 |
68 | %% Initialize an ETS table to track error and crash counters
69 | ets:new(basho_bench_errors, [protected, named_table]),
70 |
71 | %% Get the list of operations we'll be using for this test
72 | Ops = [Op || {Op, _} <- basho_bench_config:get(operations)],
73 |
74 | %% Setup stats instance for each operation -- we only track latencies on
75 | %% successful operations
76 | %%
77 | %% NOTE: Store the histograms in the process dictionary to avoid painful
78 | %% copying on state updates.
79 | [erlang:put({latencies, Op}, ?NEW_HIST) || Op <- Ops],
80 |
81 | %% Setup output file handles for dumping periodic CSV of histogram results.
82 | [erlang:put({csv_file, Op}, op_csv_file(Op)) || Op <- Ops],
83 |
84 | %% Setup output file w/ counters for total requests, errors, etc.
85 | {ok, SummaryFile} = file:open("summary.csv", [raw, binary, write]),
86 | file:write(SummaryFile, <<"elapsed, window, total, successful, failed\n">>),
87 |
88 | %% Schedule next write/reset of data
89 | ReportInterval = timer:seconds(basho_bench_config:get(report_interval)),
90 |
91 | {ok, #state{ ops = Ops,
92 | report_interval = ReportInterval,
93 | summary_file = SummaryFile }}.
94 |
95 | handle_call(run, _From, State) ->
96 | %% Schedule next report
97 | Now = now(),
98 | erlang:send_after(State#state.report_interval, self(), report),
99 | {reply, ok, State#state { start_time = Now, last_write_time = Now}};
100 |
101 | handle_call({op, Op, ok, ElapsedUs}, _From, State) ->
102 | %% Update the histogram for the op in question
103 | Hist = stats_histogram:update(ElapsedUs, erlang:get({latencies, Op})),
104 | erlang:put({latencies, Op}, Hist),
105 | {reply, ok, State};
106 | handle_call({op, Op, {error, Reason}, _ElapsedUs}, _From, State) ->
107 | increment_error_counter(Op),
108 | increment_error_counter({Op, Reason}),
109 | {reply, ok, State#state { errors_since_last_report = true }}.
110 |
111 | handle_cast(_, State) ->
112 | {noreply, State}.
113 |
114 | handle_info(report, State) ->
115 | %% Determine how much time has elapsed (seconds) since our last report
116 | Now = now(),
117 | Elapsed = trunc(timer:now_diff(Now, State#state.start_time) / 1000000),
118 | Window = trunc(timer:now_diff(Now, State#state.last_write_time) / 1000000),
119 |
120 | %% Time to report latency data to our CSV files
121 | {Oks, Errors} = lists:foldl(fun(Op, {TotalOks, TotalErrors}) ->
122 | {Oks, Errors} = report_latency(Elapsed, Window, Op),
123 | {TotalOks + Oks, TotalErrors + Errors}
124 | end, {0,0}, State#state.ops),
125 |
126 | %% Reset latency histograms
127 | [erlang:put({latencies, Op}, ?NEW_HIST) || Op <- State#state.ops],
128 |
129 | %% Write summary
130 | file:write(State#state.summary_file,
131 | io_lib:format("~w, ~w, ~w, ~w, ~w\n",
132 | [Elapsed,
133 | Window,
134 | Oks + Errors,
135 | Oks,
136 | Errors])),
137 |
138 | %% Dump current error counts to console
139 | case (State#state.errors_since_last_report) of
140 | true ->
141 | ?INFO("Errors:~p\n", [ets:tab2list(basho_bench_errors)]);
142 | false ->
143 | ok
144 | end,
145 |
146 | %% Schedule next report
147 | erlang:send_after(State#state.report_interval, self(), report),
148 | {noreply, State#state { last_write_time = Now, errors_since_last_report = false }}.
149 |
150 | terminate(_Reason, State) ->
151 | [ok = file:close(F) || {{csv_file, _}, F} <- erlang:get()],
152 | ok = file:close(State#state.summary_file),
153 |
154 | ?CONSOLE("~p\n", [ets:tab2list(basho_bench_errors)]),
155 | ok.
156 |
157 | code_change(_OldVsn, State, _Extra) ->
158 | {ok, State}.
159 |
160 |
161 |
162 | %% ====================================================================
163 | %% Internal functions
164 | %% ====================================================================
165 |
166 | op_csv_file(Op) ->
167 | Fname = lists:concat([Op, "_latencies.csv"]),
168 | {ok, F} = file:open(Fname, [raw, binary, write]),
169 | ok = file:write(F, <<"elapsed, window, n, min, mean, median, 95th, 99th, 99_9th, max, errors\n">>),
170 | F.
171 |
172 | increment_error_counter(Key) ->
173 | %% Increment the counter for this specific key. We have to deal with
174 | %% missing keys, so catch the update if it fails and init as necessary
175 | case catch(ets:update_counter(basho_bench_errors, Key, 1)) of
176 | Value when is_integer(Value) ->
177 | ok;
178 | {'EXIT', _} ->
179 | true = ets:insert_new(basho_bench_errors, {Key, 1}),
180 | ok
181 | end.
182 |
183 | error_counter(Key) ->
184 | case catch(ets:lookup_element(basho_bench_errors, Key, 2)) of
185 | {'EXIT', _} ->
186 | 0;
187 | Value ->
188 | Value
189 | end.
190 |
191 | %%
192 | %% Write latency info for a given op to the appropriate CSV. Returns the
193 | %% number of successful and failed ops in this window of time.
194 | %%
195 | report_latency(Elapsed, Window, Op) ->
196 | Hist = erlang:get({latencies, Op}),
197 | Errors = error_counter(Op),
198 | case stats_histogram:observations(Hist) > 0 of
199 | true ->
200 | {Min, Mean, Max, _, _} = stats_histogram:summary_stats(Hist),
201 | Line = io_lib:format("~w, ~w, ~w, ~w, ~.1f, ~.1f, ~.1f, ~.1f, ~.1f, ~w, ~w\n",
202 | [Elapsed,
203 | Window,
204 | stats_histogram:observations(Hist),
205 | Min,
206 | Mean,
207 | stats_histogram:quantile(0.500, Hist),
208 | stats_histogram:quantile(0.950, Hist),
209 | stats_histogram:quantile(0.990, Hist),
210 | stats_histogram:quantile(0.999, Hist),
211 | Max,
212 | Errors]);
213 | false ->
214 | ?WARN("No data for op: ~p\n", [Op]),
215 | Line = io_lib:format("~w, ~w, 0, 0, 0, 0, 0, 0, 0, 0, ~w\n",
216 | [Elapsed,
217 | Window,
218 | Errors])
219 | end,
220 | ok = file:write(erlang:get({csv_file, Op}), Line),
221 | {stats_histogram:observations(Hist), Errors}.
222 |
223 |
--------------------------------------------------------------------------------
/src/basho_bench_driver_http_raw.erl:
--------------------------------------------------------------------------------
1 | %% -------------------------------------------------------------------
2 | %%
3 | %% basho_bench: Benchmarking Suite
4 | %%
5 | %% Copyright (c) 2009-2010 Basho Techonologies
6 | %%
7 | %% This file is provided to you under the Apache License,
8 | %% Version 2.0 (the "License"); you may not use this file
9 | %% except in compliance with the License. You may obtain
10 | %% a copy of the License at
11 | %%
12 | %% http://www.apache.org/licenses/LICENSE-2.0
13 | %%
14 | %% Unless required by applicable law or agreed to in writing,
15 | %% software distributed under the License is distributed on an
16 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 | %% KIND, either express or implied. See the License for the
18 | %% specific language governing permissions and limitations
19 | %% under the License.
20 | %%
21 | %% -------------------------------------------------------------------
22 | -module(basho_bench_driver_http_raw).
23 |
24 | -export([new/1,
25 | run/4]).
26 |
27 | -include("basho_bench.hrl").
28 |
29 | -record(url, {abspath, host, port, username, password, path, protocol}).
30 |
31 | -record(state, { client_id, % Tuple client ID for HTTP requests
32 | base_urls, % Tuple of #url -- one for each IP
33 | base_urls_index, % #url to use for next request
34 | path_params }). % Params to append on the path
35 |
36 | %% ====================================================================
37 | %% API
38 | %% ====================================================================
39 |
40 | new(Id) ->
41 | %% Make sure ibrowse is available
42 | case code:which(ibrowse) of
43 | non_existing ->
44 | ?FAIL_MSG("~s requires ibrowse to be installed.\n", [?MODULE]);
45 | _ ->
46 | ok
47 | end,
48 |
49 | %% Setup client ID by base-64 encoding the ID
50 | ClientId = {'X-Riak-ClientId', base64:encode(<>)},
51 | ?DEBUG("Client ID: ~p\n", [ClientId]),
52 |
53 | application:start(ibrowse),
54 |
55 | %% The IPs, port and path we'll be testing
56 | Ips = basho_bench_config:get(http_raw_ips, ["127.0.0.1"]),
57 | Port = basho_bench_config:get(http_raw_port, 8098),
58 | Path = basho_bench_config:get(http_raw_path, "/riak/test"),
59 | Params = basho_bench_config:get(http_raw_params, ""),
60 |
61 | %% If there are multiple URLs, convert the list to a tuple so we can efficiently
62 | %% round-robin through them.
63 | case length(Ips) of
64 | 1 ->
65 | [Ip] = Ips,
66 | BaseUrls = #url { host = Ip, port = Port, path = Path },
67 | BaseUrlsIndex = 1;
68 | _ ->
69 | BaseUrls = list_to_tuple([ #url { host = Ip, port = Port, path = Path }
70 | || Ip <- Ips]),
71 | BaseUrlsIndex = random:uniform(tuple_size(BaseUrls))
72 | end,
73 |
74 | {ok, #state { client_id = ClientId,
75 | base_urls = BaseUrls,
76 | base_urls_index = BaseUrlsIndex,
77 | path_params = Params }}.
78 |
79 |
80 | run(get, KeyGen, _ValueGen, State) ->
81 | {NextUrl, S2} = next_url(State),
82 | case do_get(url(NextUrl, KeyGen, State#state.path_params)) of
83 | {not_found, _Url} ->
84 | {ok, S2};
85 | {ok, _Url, _Headers} ->
86 | {ok, S2};
87 | {error, Reason} ->
88 | {error, Reason, S2}
89 | end;
90 | run(update, KeyGen, ValueGen, State) ->
91 | {NextUrl, S2} = next_url(State),
92 | case do_get(url(NextUrl, KeyGen, State#state.path_params)) of
93 | {error, Reason} ->
94 | {error, Reason, S2};
95 |
96 | {not_found, Url} ->
97 | case do_put(Url, [], ValueGen) of
98 | ok ->
99 | {ok, S2};
100 | {error, Reason} ->
101 | {error, Reason, S2}
102 | end;
103 |
104 | {ok, Url, Headers} ->
105 | Vclock = lists:keyfind("X-Riak-Vclock", 1, Headers),
106 | case do_put(Url, [State#state.client_id, Vclock], ValueGen) of
107 | ok ->
108 | {ok, S2};
109 | {error, Reason} ->
110 | {error, Reason, S2}
111 | end
112 | end;
113 | run(insert, KeyGen, ValueGen, State) ->
114 | %% Go ahead and evaluate the keygen so that we can use the
115 | %% sequential_int_gen to do a controlled # of inserts (if we desire). Note
116 | %% that the actual insert randomly generates a key (server-side), so the
117 | %% output of the keygen is ignored.
118 | KeyGen(),
119 | {NextUrl, S2} = next_url(State),
120 | case do_post(url(NextUrl, State#state.path_params), [], ValueGen) of
121 | ok ->
122 | {ok, S2};
123 | {error, Reason} ->
124 | {error, Reason, S2}
125 | end.
126 |
127 |
128 |
129 | %% ====================================================================
130 | %% Internal functions
131 | %% ====================================================================
132 |
133 | next_url(State) when is_record(State#state.base_urls, url) ->
134 | {State#state.base_urls, State};
135 | next_url(State) when State#state.base_urls_index > tuple_size(State#state.base_urls) ->
136 | { element(1, State#state.base_urls),
137 | State#state { base_urls_index = 1 } };
138 | next_url(State) ->
139 | { element(State#state.base_urls_index, State#state.base_urls),
140 | State#state { base_urls_index = State#state.base_urls_index + 1 }}.
141 |
142 | url(BaseUrl, Params) ->
143 | BaseUrl#url { path = lists:concat([BaseUrl#url.path, Params]) }.
144 | url(BaseUrl, KeyGen, Params) ->
145 | BaseUrl#url { path = lists:concat([BaseUrl#url.path, '/', KeyGen(), Params]) }.
146 |
147 |
148 | do_get(Url) ->
149 | case send_request(Url, [], get, [], [{response_format, binary}]) of
150 | {ok, "404", _Headers, _Body} ->
151 | {not_found, Url};
152 | {ok, "300", Headers, _Body} ->
153 | {ok, Url, Headers};
154 | {ok, "200", Headers, _Body} ->
155 | {ok, Url, Headers};
156 | {ok, Code, _Headers, _Body} ->
157 | {error, {http_error, Code}};
158 | {error, Reason} ->
159 | {error, Reason}
160 | end.
161 |
162 | do_put(Url, Headers, ValueGen) ->
163 | case send_request(Url, Headers ++ [{'Content-Type', 'application/octet-stream'}],
164 | put, ValueGen(), [{response_format, binary}]) of
165 | {ok, "204", _Header, _Body} ->
166 | ok;
167 | {ok, Code, _Header, _Body} ->
168 | {error, {http_error, Code}};
169 | {error, Reason} ->
170 | {error, Reason}
171 | end.
172 |
173 | do_post(Url, Headers, ValueGen) ->
174 | case send_request(Url, Headers ++ [{'Content-Type', 'application/octet-stream'}],
175 | post, ValueGen(), [{response_format, binary}]) of
176 | {ok, "201", _Header, _Body} ->
177 | ok;
178 | {ok, "204", _Header, _Body} ->
179 | ok;
180 | {ok, Code, _Header, _Body} ->
181 | {error, {http_error, Code}};
182 | {error, Reason} ->
183 | {error, Reason}
184 | end.
185 |
186 |
187 | connect(Url) ->
188 | case erlang:get({ibrowse_pid, Url#url.host}) of
189 | undefined ->
190 | {ok, Pid} = ibrowse_http_client:start({Url#url.host, Url#url.port}),
191 | erlang:put({ibrowse_pid, Url#url.host}, Pid),
192 | Pid;
193 | Pid ->
194 | case is_process_alive(Pid) of
195 | true ->
196 | Pid;
197 | false ->
198 | erlang:erase({ibrowse_pid, Url#url.host}),
199 | connect(Url)
200 | end
201 | end.
202 |
203 |
204 | disconnect(Url) ->
205 | case erlang:get({ibrowse_pid, Url#url.host}) of
206 | undefined ->
207 | ok;
208 | OldPid ->
209 | catch(ibrowse_http_client:stop(OldPid))
210 | end,
211 | erlang:erase({ibrowse_pid, Url#url.host}),
212 | ok.
213 |
214 |
215 | send_request(Url, Headers, Method, Body, Options) ->
216 | send_request(Url, Headers, Method, Body, Options, 3).
217 |
218 | send_request(_Url, _Headers, _Method, _Body, _Options, 0) ->
219 | {error, max_retries};
220 | send_request(Url, Headers, Method, Body, Options, Count) ->
221 | Pid = connect(Url),
222 | case catch(ibrowse_http_client:send_req(Pid, Url, Headers, Method, Body, Options, 5000)) of
223 | {ok, Status, RespHeaders, RespBody} ->
224 | {ok, Status, RespHeaders, RespBody};
225 |
226 | Error ->
227 | disconnect(Url),
228 | case should_retry(Error) of
229 | true ->
230 | send_request(Url, Headers, Method, Body, Options, Count-1);
231 |
232 | false ->
233 | normalize_error(Method, Error)
234 | end
235 | end.
236 |
237 |
238 | should_retry({error, send_failed}) -> true;
239 | should_retry({error, connection_closed}) -> true;
240 | should_retry({'EXIT', {normal, _}}) -> true;
241 | should_retry({'EXIT', {noproc, _}}) -> true;
242 | should_retry(_) -> false.
243 |
244 | normalize_error(Method, {'EXIT', {timeout, _}}) -> {error, {Method, timeout}};
245 | normalize_error(Method, {'EXIT', Reason}) -> {error, {Method, 'EXIT', Reason}};
246 | normalize_error(Method, {error, Reason}) -> {error, {Method, Reason}}.
247 |
--------------------------------------------------------------------------------
/src/basho_bench_worker.erl:
--------------------------------------------------------------------------------
1 | %% -------------------------------------------------------------------
2 | %%
3 | %% basho_bench: Benchmarking Suite
4 | %%
5 | %% Copyright (c) 2009-2010 Basho Techonologies
6 | %%
7 | %% This file is provided to you under the Apache License,
8 | %% Version 2.0 (the "License"); you may not use this file
9 | %% except in compliance with the License. You may obtain
10 | %% a copy of the License at
11 | %%
12 | %% http://www.apache.org/licenses/LICENSE-2.0
13 | %%
14 | %% Unless required by applicable law or agreed to in writing,
15 | %% software distributed under the License is distributed on an
16 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 | %% KIND, either express or implied. See the License for the
18 | %% specific language governing permissions and limitations
19 | %% under the License.
20 | %%
21 | %% -------------------------------------------------------------------
22 | -module(basho_bench_worker).
23 |
24 | -behaviour(gen_server).
25 |
26 | %% API
27 | -export([start_link/2,
28 | run/1,
29 | stop/1]).
30 |
31 | %% gen_server callbacks
32 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
33 | terminate/2, code_change/3]).
34 |
35 | -record(state, { id,
36 | keygen,
37 | valgen,
38 | driver,
39 | driver_state,
40 | ops,
41 | ops_len,
42 | rng_seed,
43 | parent_pid,
44 | worker_pid,
45 | sup_id}).
46 |
47 | -include("basho_bench.hrl").
48 |
49 | %% ====================================================================
50 | %% API
51 | %% ====================================================================
52 |
53 | start_link(SupChild, Id) ->
54 | gen_server:start_link(?MODULE, [SupChild, Id], []).
55 |
56 | run(Pids) ->
57 | [ok = gen_server:call(Pid, run) || Pid <- Pids],
58 | ok.
59 |
60 | stop(Pids) ->
61 | [ok = gen_server:call(Pid, stop) || Pid <- Pids],
62 | ok.
63 |
64 | %% ====================================================================
65 | %% gen_server callbacks
66 | %% ====================================================================
67 |
68 | init([SupChild, Id]) ->
69 | %% Setup RNG seed for worker sub-process to use; incorporate the ID of
70 | %% the worker to ensure consistency in load-gen
71 | %%
72 | %% NOTE: If the worker process dies, this obviously introduces some entroy
73 | %% into the equation since you'd be restarting the RNG all over.
74 | process_flag(trap_exit, true),
75 | {A1, A2, A3} = basho_bench_config:get(rng_seed),
76 | RngSeed = {A1+Id, A2+Id, A3+Id},
77 |
78 | %% Pull all config settings from environment
79 | Driver = basho_bench_config:get(driver),
80 | Ops = ops_tuple(),
81 |
82 | %% Finally, initialize key and value generation. We pass in our ID to the
83 | %% initialization to enable (optional) key/value space partitioning
84 | KeyGen = basho_bench_keygen:new(basho_bench_config:get(key_generator), Id),
85 | ValGen = basho_bench_valgen:new(basho_bench_config:get(value_generator), Id),
86 |
87 | State = #state { id = Id, keygen = KeyGen, valgen = ValGen,
88 | driver = Driver,
89 | ops = Ops, ops_len = size(Ops),
90 | rng_seed = RngSeed,
91 | parent_pid = self(),
92 | sup_id = SupChild},
93 |
94 | %% Use a dedicated sub-process to do the actual work. The work loop may need
95 | %% to sleep or otherwise delay in a way that would be inappropriate and/or
96 | %% inefficient for a gen_server. Furthermore, we want the loop to be as
97 | %% tight as possible for peak load generation and avoid unnecessary polling
98 | %% of the message queue.
99 | %%
100 | %% Link the worker and the sub-process to ensure that if either exits, the
101 | %% other goes with it.
102 | WorkerPid = spawn_link(fun() -> worker_init(State) end),
103 | WorkerPid ! {init_driver, self()},
104 | receive
105 | driver_ready ->
106 | ok
107 | end,
108 |
109 | %% If the system is marked as running this is a restart; queue up the run
110 | %% message for this worker
111 | case basho_bench_app:is_running() of
112 | true ->
113 | ?WARN("Restarting crashed worker.\n", []),
114 | gen_server:cast(self(), run);
115 | false ->
116 | ok
117 | end,
118 |
119 | {ok, State#state { worker_pid = WorkerPid }}.
120 |
121 | handle_call(run, _From, State) ->
122 | State#state.worker_pid ! run,
123 | {reply, ok, State}.
124 |
125 | handle_cast(run, State) ->
126 | State#state.worker_pid ! run,
127 | {noreply, State}.
128 |
129 | handle_info({'EXIT', _Pid, Reason}, State) ->
130 | case Reason of
131 | normal ->
132 | %% Clean shutdown of the worker; spawn a process to terminate this
133 | %% process via the supervisor API and make sure it doesn't restart.
134 | spawn(fun() -> stop_worker(State#state.sup_id) end),
135 | {noreply, State};
136 |
137 | _ ->
138 | %% Worker process exited for some other reason; stop this process
139 | %% as well so that everything gets restarted by the sup
140 | {stop, normal, State}
141 | end.
142 |
143 | terminate(_Reason, _State) ->
144 | ok.
145 |
146 | code_change(_OldVsn, State, _Extra) ->
147 | {ok, State}.
148 |
149 |
150 |
151 | %% ====================================================================
152 | %% Internal functions
153 | %% ====================================================================
154 |
155 | %%
156 | %% Stop a worker process via the supervisor and terminate the app
157 | %% if there are no workers remaining
158 | %%
159 | %% WARNING: Must run from a process other than the worker!
160 | %%
161 | stop_worker(SupChild) ->
162 | ok = basho_bench_sup:stop_child(SupChild),
163 | case basho_bench_sup:workers() of
164 | [] ->
165 | %% No more workers -- stop the system
166 | basho_bench_app:stop();
167 | _ ->
168 | ok
169 | end.
170 |
171 | %%
172 | %% Expand operations list into tuple suitable for weighted, random draw
173 | %%
174 | ops_tuple() ->
175 | Ops = [lists:duplicate(Count, Op) || {Op, Count} <- basho_bench_config:get(operations)],
176 | list_to_tuple(lists:flatten(Ops)).
177 |
178 |
179 | worker_init(State) ->
180 | random:seed(State#state.rng_seed),
181 | worker_idle_loop(State).
182 |
183 | worker_idle_loop(State) ->
184 | Driver = State#state.driver,
185 | receive
186 | {init_driver, Caller} ->
187 | %% Spin up the driver implementation
188 | case catch(Driver:new(State#state.id)) of
189 | {ok, DriverState} ->
190 | Caller ! driver_ready,
191 | ok;
192 | Error ->
193 | DriverState = undefined, % Make erlc happy
194 | ?FAIL_MSG("Failed to initialize driver ~p: ~p\n", [Driver, Error])
195 | end,
196 | worker_idle_loop(State#state { driver_state = DriverState });
197 | run ->
198 | case basho_bench_config:get(mode) of
199 | max ->
200 | ?INFO("Starting max worker: ~p\n", [self()]),
201 | max_worker_run_loop(State);
202 | {rate, Rate} ->
203 | %% Calculate mean interarrival time in in milliseconds. A
204 | %% fixed rate worker can generate (at max) only 1k req/sec.
205 | MeanArrival = 1000 / Rate,
206 | ?INFO("Starting ~w ms/req fixed rate worker: ~p\n", [MeanArrival, self()]),
207 | rate_worker_run_loop(State, 1 / MeanArrival)
208 | end
209 | end.
210 |
211 | worker_next_op(State) ->
212 | Next = element(random:uniform(State#state.ops_len), State#state.ops),
213 | Start = now(),
214 | Result = (catch (State#state.driver):run(Next, State#state.keygen, State#state.valgen,
215 | State#state.driver_state)),
216 | ElapsedUs = timer:now_diff(now(), Start),
217 | case Result of
218 | {ok, DriverState} ->
219 | %% Success
220 | basho_bench_stats:op_complete(Next, ok, ElapsedUs),
221 | {ok, State#state { driver_state = DriverState}};
222 |
223 | {error, Reason, DriverState} ->
224 | %% Driver encountered a recoverable error
225 | basho_bench_stats:op_complete(Next, {error, Reason}, ElapsedUs),
226 | {ok, State#state { driver_state = DriverState}};
227 |
228 | {'EXIT', Reason} ->
229 | %% Driver crashed, generate a crash error and terminate. This will take down
230 | %% the corresponding worker which will get restarted by the appropriate supervisor.
231 | basho_bench_stats:op_complete(Next, {error, crash}, ElapsedUs),
232 | ?DEBUG("Driver ~p crashed: ~p\n", [State#state.driver, Reason]),
233 | crash;
234 |
235 | {stop, Reason} ->
236 | %% Driver (or something within it) has requested that this worker
237 | %% terminate cleanly.
238 | ?INFO("Driver ~p (~p) has requested stop: ~p\n", [State#state.driver, self(), Reason]),
239 | normal
240 | end.
241 |
242 | max_worker_run_loop(State) ->
243 | case worker_next_op(State) of
244 | {ok, State2} ->
245 | max_worker_run_loop(State2);
246 | ExitReason ->
247 | exit(ExitReason)
248 | end.
249 |
250 | rate_worker_run_loop(State, Lambda) ->
251 | %% Delay between runs using exponentially distributed delays to mimic
252 | %% queue.
253 | timer:sleep(trunc(stats_rv:exponential(Lambda))),
254 | case worker_next_op(State) of
255 | {ok, State2} ->
256 | rate_worker_run_loop(State2, Lambda);
257 | ExitReason ->
258 | exit(ExitReason)
259 | end.
260 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 |
--------------------------------------------------------------------------------
/docs/Documentation.org:
--------------------------------------------------------------------------------
1 | #+SETUPFILE: html-style.org
2 | #+SETUPFILE: pdf-style.org
3 | #+TITLE: Basho Bench 0.1 Documentation
4 | #+AUTHOR: Dave Smith (code) / Rusty Klophaus (docs)
5 | #+EMAIL: dizzyd@basho.com, rusty@basho.com
6 |
7 | * Overview
8 | ** Purpose
9 |
10 | Basho Bench is a benchmarking tool created to conduct accurate and
11 | repeatable performance tests and stress tests, and produce performance graphs.
12 |
13 | Originally developed by Dave Smith (Dizzy) to benchmark Riak, Basho's
14 | key/value datastore, it exposes a pluggable driver interface and has
15 | been extended to serve as a benchmarking tool against a variety of
16 | projects. New drivers can be written in Erlang and are generally
17 | less than 200 lines of code.
18 |
19 | ** How does it work?
20 |
21 | When Basho Bench starts (basho\_bench.erl), it reads the configuration
22 | (basho\_bench\_config.erl), creates a new results directory, then sets up the
23 | test. (basho\_bench\_app.erl/basho\_bench\_sup.erl)
24 |
25 | During test setup, Basho Bench creates:
26 |
27 | + One *log process* (basho\_bench\_log.erl). During startup, this
28 | creates a new /log.txt/ file in the current results directory, to
29 | which output is logged at the specified logging level.
30 |
31 | + One *stats process* (basho\_bench\_stats.erl). This receives notifications
32 | when an operation completes, plus the elapsed time of the operation, and
33 | stores it in a histogram. At regular intervals, the histograms are dumped
34 | to /summary.csv/ as well as operation-specific latency CSVs
35 | (e.g. /put_latencies.csv/ for the 'put' operation).
36 |
37 | + N *workers*, where N is specified by the /concurrent/ configuration
38 | setting. (basho\_bench\_worker.erl). The worker process wraps a driver
39 | module, specified by the /driver/ configuration setting. The driver is
40 | randomly invoked using the distribution of operations as specified by the
41 | /operation/ configuration setting. The rate at which the driver invokes
42 | operations is governed by the /mode/ setting.
43 |
44 | Once these processes have been created and initialized, Basho Bench sends a
45 | run command to all worker processes, causing them to begin the test. Each worker
46 | is initialized with a common seed value for random number generation to ensure
47 | that the generated workload is reproducible at a later date.
48 |
49 | During the test, the workers repeatedly call /driver/:run/4, passing in the
50 | next operation to run, a keygen function, a valuegen function, and the last
51 | state of the driver. The worker process times the operation, and reports this
52 | to the stats process when the operation has completed.
53 |
54 | Finally, once the test has been run for the duration specified in the config
55 | file, all workers and stats processes are terminated and the benchmark
56 | ends. The measured latency and throughput of the test can be found in
57 | /./tests/current/. Previous results are in timestamped directories of the
58 | form /./tests/YYYYMMDD-HHMMSS/.
59 |
60 | * Installation
61 |
62 | ** Prerequisites
63 |
64 | + Erlang R13B03 - http://erlang.org/download.html
65 | + R - http://www.r-project.org/ (for graphing)
66 |
67 | ** Building from Source
68 |
69 | Basho Bench is currently available as source code only. To get the
70 | latest code, clone the basho\_bench repository:
71 |
72 | : hg clone ssh://hg@bitbucket.org/basho/basho_bench
73 | : cd basho_bench
74 | : make
75 |
76 | * Usage
77 |
78 | Run basho\_bench:
79 |
80 | : ./basho_bench myconfig.config
81 |
82 | This will generate results in /tests/current/. You will need to
83 | create a configuration file. The recommended approach is to start
84 | from a file in the /examples/ directory and modify settings using the
85 | /Configuration/ section below for reference.
86 |
87 | Note that currently you must run the basho\_bench script from the directory
88 | where it was built to ensure that the necessary dependencies are available.
89 |
90 | * Generating Benchmark Graphs
91 |
92 | The output of basho\_bench can be used to create graphs showing:
93 |
94 | + Throughput - Operations per second over the duration of the test.
95 |
96 | + Latency at 99th percentile, 99.9th percentile and max latency for
97 | the selected operations.
98 |
99 | + Median latency, mean latency, and 95th percentile latency for the
100 | selected operations.
101 |
102 | ** Prerequisites
103 |
104 | The R statistics language is needed to generate graphs.
105 |
106 | + More information: http://www.r-project.org/.
107 |
108 | + Download R: http://cran.r-project.org/mirrors.html
109 |
110 | Follow the instructions for your platform to install R.
111 |
112 | ** Generating a Graphs
113 |
114 | To generate a benchmark graph against the current results, run:
115 |
116 | : make results
117 |
118 | This will create a results file in /tests/current/summary.png/.
119 |
120 | You can also run this manually:
121 |
122 | : priv/summary.r -i tests/current
123 |
124 | * Configuration
125 |
126 | Basho Bench ships with a number of sample configuration files,
127 | available in the /examples/ directory.
128 |
129 | ** Global Config Settings
130 | *** mode
131 |
132 | The *mode* setting controls the rate at which workers invoke the /driver/:run/4
133 | function with a new operation. There are two possible values:
134 |
135 | - max :: generate as many ops per second as possible
136 |
137 | - {rate, N} :: generate N ops per second, with exponentially distributed
138 | interarrival times.
139 |
140 | Note that this setting is applied to each driver independently. For example, if
141 | /{rate, 5}/ is used with 3 concurrent workers, basho\_bench will be generating
142 | 15 (i.e. 5 * 3) operations per second.
143 |
144 | : % Run at max, ie: as quickly as possible.
145 | : {mode, max}
146 |
147 | : % Run 15 operations per second.
148 | : {mode, {rate, 15}}
149 |
150 | *** concurrent
151 |
152 | The number of concurrent worker processes. The default is 3 worker processes.
153 |
154 | : % Run 10 concurrent processes.
155 | : {concurrent, 10}
156 |
157 | *** duration
158 |
159 | The duration of the test, in minutes. The default is 5 minutes.
160 |
161 | : % Run the test for one hour.
162 | : {duration, 60}
163 |
164 | *** operations
165 |
166 | The possible operations that the driver will run, plus their
167 | "weight" or likelihood of being run. Default is =[{get,4},
168 | {put,4}, {delete, 1}]= which means that out of every 9 operations,
169 | 'get' will be called four times, 'put' will called four times, and
170 | 'delete' will be called once, on average.
171 |
172 | : % Run 80% gets, 20% puts.
173 | : {operations, [{get, 4}, {put, 1}]}.
174 |
175 | Operations are defined on a *per-driver* basis. Not all drivers will
176 | implement the "get"/"put" operations discussed above. Consult the driver
177 | source to determine the valid operations.
178 |
179 | If a driver does not support a specified operation ("askdfput" in this
180 | example) you may see errors like:
181 |
182 | : DEBUG:Driver basho_bench_driver_null crashed: {function_clause,
183 | : [{basho_bench_driver_null,run,
184 | : [asdfput,
185 | : #Fun,
186 | : #Fun,
187 | : undefined]},
188 | : {basho_bench_worker,
189 | : worker_next_op,1},
190 | : {basho_bench_worker,
191 | : max_worker_run_loop,1}]}
192 |
193 |
194 | *** driver
195 |
196 | The module name of the driver that basho\_bench will use to generate load. A
197 | driver may simply invoke code in-process (such as when measuring the
198 | performance of innostore or DETS) or may open network connections and
199 | generate load on a remote system (such as when testing a Riak
200 | server/cluster).
201 |
202 | Available drivers include:
203 |
204 | + basho\_bench\_driver\_http\_raw :: Uses Riak's HTTP interface to
205 | get/put/delete data on a Riak server
206 | + basho\_bench\_driver\_riakc\_pb :: Uses Riak's Protocol Buffers interface
207 | to get/put/delete data on a Riak server
208 | + basho\_bench\_driver\_riakclient :: Uses Riak's Dist. Erlang interface to
209 | get/put/delete data on a Riak server
210 | + basho\_bench\_driver\_bitcask :: Directly invokes the Bitcask API
211 | + basho\_bench\_driver\_dets :: Directly invokes the DETS API
212 | + basho\_bench\_driver\_innostore :: Directly invokes the Innostore API
213 |
214 | On invocation of the /driver/:run/4 method, the driver may return one of the
215 | following results:
216 |
217 | + ={ok, NewState}= :: operation completed successfully
218 | + ={error, Reason, NewState}= :: operation failed but the driver can continue
219 | processing (i.e. recoverable error)
220 | + ={stop, Reason}= :: operation failed; driver can't/won't continue
221 | processing
222 | + ={'EXIT', Reason}= :: operation failed; driver crashed
223 |
224 |
225 | *** code\_paths
226 |
227 | Some drivers need additional Erlang code in order to run. Specify
228 | the paths to this code using the *code\_paths* configuration
229 | setting.
230 |
231 | As noted previously, basho\_bench /must/ be run in the directory it was
232 | built, for dependency reasons. *code\_paths* should include, minimally, a
233 | reference to "deps/stats" which is the library that basho\_bench uses for
234 | various statistical purposes.
235 |
236 | For example:
237 |
238 | : {code_paths, [
239 | : "deps/stats",
240 | : "../riak_src/apps/riak_kv",
241 | : "../riak_src/apps/riak_core"]}.
242 |
243 |
244 | *** key\_generator
245 |
246 | The generator function to use for creating keys. Generators are defined in
247 | /basho\_bench\_keygen.erl/. Available generators include:
248 |
249 | + {sequential\_int, MaxKey} :: generates integers from 0..MaxKey in order
250 | and then stops the system. Note that each instance of this keygen is
251 | specific to a worker.
252 |
253 | + {sequential\_int\_bin, MaxKey} :: same as above, but the result from the
254 | function is a 32-bit binary encoding of the integer.
255 |
256 | + {sequential\_int\_str, MaxKey} :: same as /sequential\_int/, but the
257 | result from the function is encoded as a string.
258 |
259 | + {uniform\_int, MaxKey} :: selects an integer from uniform distribution of
260 | 0..MaxKey. I.e. all integers are equally probable.
261 |
262 | + {uniform\_int\_bin, MaxKey} :: same as above, but the result of the
263 | function is a 32-bit binary encoding of the integer.
264 |
265 | + {uniform\_int\_str, MaxKey} :: same as /uniform\_int/ , but the result
266 | from the function is encoded as a string.
267 |
268 | + {pareto\_int, MaxKey} :: selects an integer from a Pareto distribution,
269 | such that 20% of the available keys get selected 80% of the time. Note
270 | that the current implementation of this generator MAY yield values
271 | larger than MaxKey due to the mathematical properties of the Pareto
272 | distribution.
273 |
274 | + {pareto\_int\_bin, MaxKey} :: same as /pareto\_int/, but the result from
275 | the function is a 32-bit binary encoding of the integer.
276 |
277 | The default key generator is ={uniform_int, 100000}=.
278 |
279 | Examples:
280 |
281 | : % Use a randomly selected integer between 1 and 10,000
282 | : {key_generator, {uniform_int, 10000}}.
283 |
284 | : % Use a randomly selected integer between 1 and 10,000, as binary.
285 | : {key_generator, {uniform_int_bin, 10000}}.
286 |
287 | : % Use a pareto distributed integer between 1 and 10,000; values < 2000
288 | : % will be returned 80% of the time.
289 | : {key_generator, {pareto_int, 10000}}.
290 |
291 | *** value\_generator
292 |
293 | The generator function to use for creating values. Generators are defined in
294 | /basho\_bench\_valgen.erl/. Available generators include:
295 |
296 | + {fixed\_bin, Size} :: generates a random binary of Size bytes. Every
297 | binary is the same size, but varies in content.
298 |
299 | + {exponential\_bin, MinSize, Mean} :: generate a random binary which has an
300 | exponentially-distributed size. Most values will be approximately
301 | MinSize + Mean bytes in size, with a long-tail of larger values.
302 |
303 | The default value generator is ={value\_generator, {fixed\_bin, 100}}=.
304 |
305 | Examples:
306 |
307 | : % Generate a fixed size random binary of 512 bytes
308 | : {value_generator, {fixed_bin, 512}}.
309 |
310 | : % Generate a random binary whose size is exponentially distributed
311 | : % starting at 1000 bytes and a mean of 2000 bytes
312 | : {value_generator, {exponential_bin, 1000, 2000}}.
313 |
314 | *** rng\_seed
315 |
316 | The initial random seed to use. This is explicitly seeded, rather than
317 | seeded from the current time, so that a test can be run in a predictable,
318 | repeatable fashion.
319 |
320 | Default is ={rng_seed, {42, 23, 12}}=.
321 |
322 | : % Seed to {12, 34, 56}
323 | : {rng_seed, {12, 34, 56}}.
324 |
325 | *** log\_level
326 |
327 | The *log\_level* setting determines which messages Basho Bench will
328 | log to the console and to disk.
329 |
330 | Default level is *debug*.
331 |
332 | Valid levels are:
333 |
334 | + debug
335 | + info
336 | + warn
337 | + error
338 |
339 | *** report\_interval
340 |
341 | How often, in seconds, should the stats process write histogram
342 | data to disk. Default is 10 seconds.
343 |
344 | *** test\_dir
345 |
346 | The directory in which to write result data. The default is
347 | /tests/.
348 |
349 |
350 | ** basho\_bench\_driver\_riakclient Settings
351 |
352 | These configuration settings apply to the
353 | /basho\_bench\_driver\_riakclient/ driver.
354 |
355 | *** riakclient\_nodes
356 |
357 | List of Riak nodes to use for testing.
358 |
359 | : {riakclient_nodes, ['riak1@127.0.0.1', 'riak2@127.0.0.1']}.
360 |
361 | *** riakclient\_cookie
362 |
363 | The Erlang cookie to use to connect to Riak clients. Default is ='riak'=.
364 |
365 | : {riakclient_cookie, riak}.
366 |
367 | *** riakclient\_mynode
368 |
369 | The name of the local node. This is passed into
370 | =net_kernel:start/1= (http://erlang.org/doc/man/net_kernel.html).
371 |
372 | : {riakclient_mynode, ['basho_bench@127.0.0.1', longnames]}.
373 |
374 | *** riakclient\_replies
375 |
376 | This value is used for R-values during a get operation, and
377 | W-values during a put operation.
378 |
379 | : % Expect 1 reply.
380 | : {riakclient_replies, 1}.
381 |
382 | *** riakclient\_bucket
383 |
384 | The Riak bucket to use for reading and writing values. Default is =<<"test">>=.
385 |
386 | : % Use the "bench" bucket.
387 | : {riakclient_bucket, <<"bench">>}.
388 |
389 | ** basho\_bench\_driver\_dets Settings
390 |
391 | Not yet documented.
392 |
393 | ** basho\_bench\_driver\_http\_raw Settings
394 |
395 | *** http\_raw\_ips
396 | List of IP addresses to connect the workers to. Each worker makes requests to each
397 | IP in a round-robin fashion.
398 |
399 | Default is ={http_raw_ips, ["127.0.0.1"]}=
400 |
401 | % Connect to a cluster of machines in the 10.x network
402 | {http_raw_ips, ["10.0.0.1", "10.0.0.2", "10.0.0.3"]}.
403 |
404 |
405 | *** http_raw_port
406 | Select the default port to connect on for the HTTP server.
407 |
408 | Default is ={http_raw_port, 8098}.=
409 |
410 | % Connect on port 8090
411 | {http_raw_port, 8090}.
412 |
413 | *** http_raw_path
414 | Base path to use for accessing riak - usually "/riak/"
415 |
416 | Defaults is ={http_raw_path, "/riak/test"}.=
417 |
418 | % Place test data in another_bucket
419 | {http_raw_path, "/riak/another_bucket"}.
420 |
421 | *** http_raw_params
422 | Additional parameters to add to the end of the URL. This can be used to set
423 | riak r/w/dw/rw parameters as as desired.
424 |
425 | Default is ={http_raw_params, ""}.=
426 |
427 | % Set R=1, W=1 for testing a system with n_val set to 1
428 | {http_raw_params, "?r=1&w=1"}.
429 |
430 | * Custom Driver
431 |
432 | A custom driver must expose the following callbacks.
433 |
434 | : % Create the worker.
435 | : % ID is an integer.
436 | : new(ID) -> {ok, State} or {error, Reason}.
437 |
438 | : % Run an operation.
439 | : run(Op, KeyGen, ValueGen, State) -> {ok, NewState} or {error, Reason, NewState}
440 |
441 | See the existing drivers for more details.
442 |
443 |
444 |
--------------------------------------------------------------------------------