2012-08-15 17:27:32 +00:00
|
|
|
# Riak Test
|
2012-09-19 21:15:13 +00:00
|
|
|
Welcome to the exciting world of `riak_test`.
|
2012-08-15 17:27:32 +00:00
|
|
|
|
2012-09-18 18:27:14 +00:00
|
|
|
## What is Riak Test?
|
|
|
|
|
|
|
|
`riak_test` is a system for testing Riak clusters. Tests are written
|
|
|
|
in Erlang, and can interact with the cluster using distributed Erlang.
|
|
|
|
|
|
|
|
### How does it work?
|
|
|
|
|
2013-01-23 16:43:19 +00:00
|
|
|
`riak_test` runs tests in a sandbox, typically `$HOME/rt/riak`. The sanbox
|
2012-09-18 18:27:14 +00:00
|
|
|
uses `git` to reset back to a clean state after tests are run. The
|
2013-01-23 16:43:19 +00:00
|
|
|
contents of `$HOME/rt/riak` might look something like this:
|
2012-09-18 18:27:14 +00:00
|
|
|
|
|
|
|
```
|
2013-01-23 16:43:19 +00:00
|
|
|
$ ls $HOME/rt/riak
|
2012-09-18 18:27:14 +00:00
|
|
|
current riak-1.0.3 riak-1.1.4 riak-1.2.0
|
|
|
|
```
|
|
|
|
|
|
|
|
Inside each of these directories is a `dev` folder, typically
|
|
|
|
created with your normal `make [stage]devrel`. So how does
|
|
|
|
this sandbox get populated to begin with?
|
|
|
|
|
|
|
|
You'll create another directory that will contain full builds
|
2012-09-18 18:40:41 +00:00
|
|
|
of different version of Riak for your platform. Typically this directory
|
2012-09-19 21:15:13 +00:00
|
|
|
has been `~/test-releases` but it can be called anything and be anywhere
|
|
|
|
that you'd like. The `dev/` directory from each of these
|
2013-01-23 16:43:19 +00:00
|
|
|
releases will be copied into the sandbox (`$HOME/rt/riak`).
|
2012-09-18 18:27:14 +00:00
|
|
|
There are helper scripts in `bin/` which will
|
2013-01-23 16:43:19 +00:00
|
|
|
help you get both `~/test-releases` and `$HOME/rt/riak` all set up. A full
|
2012-09-18 18:27:14 +00:00
|
|
|
tutorial for using them exists further down in this README.
|
|
|
|
|
2013-01-23 16:43:19 +00:00
|
|
|
There is one folder in `$HOME/rt/riak` that does not come from
|
2012-09-18 18:27:14 +00:00
|
|
|
`~/test-releases`: `current`. The `current` folder can refer
|
|
|
|
to any version of Riak, but is typically used for something
|
|
|
|
like the `master` branch, a feature branch, or a release candidate.
|
2013-01-23 16:43:19 +00:00
|
|
|
The `$HOME/rt/riak/current` dev release gets populated from a devrel of Riak
|
2012-09-18 18:27:14 +00:00
|
|
|
that can come from anywhere, but is usually your 'normal' git checkout
|
|
|
|
of Riak. The `bin/rtdev-current.sh` can be run from within that folder
|
2013-01-23 16:43:19 +00:00
|
|
|
to copy `dev/` into `$HOME/rt/riak/current`.
|
2012-09-18 18:27:14 +00:00
|
|
|
|
|
|
|
Once you have everything set up (again, instructions for this are below),
|
|
|
|
you'll want to run and write tests. This repository also holds code for
|
|
|
|
an Erlang application called `riak_test`. The actual tests exist in
|
|
|
|
the `test/` directory.
|
|
|
|
|
2012-08-22 15:39:34 +00:00
|
|
|
## Bootstraping Your Test Environment
|
|
|
|
|
2012-09-19 21:15:13 +00:00
|
|
|
Running tests against a
|
2012-09-17 21:16:59 +00:00
|
|
|
development version of Riak is just one of the things that you can do
|
|
|
|
with riak_test. You can also test things involving upgrading from
|
|
|
|
previous versions of Riak. Together, we'll get your test environment
|
|
|
|
up and running. Scripts to help in this process are located in the
|
|
|
|
`bin` directory of this project.
|
2012-08-22 15:39:34 +00:00
|
|
|
|
2012-09-17 21:26:47 +00:00
|
|
|
### rtdev-all.sh
|
|
|
|
|
2012-09-17 23:28:32 +00:00
|
|
|
This script is for the lazy. It performs all of the setup steps described
|
|
|
|
in the other scripts, including installing the current "master" branch from
|
2012-09-19 21:49:28 +00:00
|
|
|
Github into "current". The releases will be built in your current working
|
2012-09-19 21:15:13 +00:00
|
|
|
directory, so create an empty one in a place you'd like to store these
|
|
|
|
builds for posterity, so that you don't have to rebuild them if your
|
2013-01-23 16:43:19 +00:00
|
|
|
installation path (`$HOME/rt/riak` by the way this script installs it) gets into
|
|
|
|
a bad state.
|
2012-09-19 21:15:13 +00:00
|
|
|
|
2013-01-23 16:43:19 +00:00
|
|
|
If you do want to restore your `$HOME/rt/riak` folder to factory condition, see
|
2012-09-19 21:15:13 +00:00
|
|
|
`rtdev-setup-releases.sh` and if you want to change the current riak under
|
|
|
|
test, see `rtdev-current.sh`.
|
2012-09-17 21:26:47 +00:00
|
|
|
|
2012-08-22 15:39:34 +00:00
|
|
|
### rtdev-build-releases.sh
|
|
|
|
|
2012-09-17 21:16:59 +00:00
|
|
|
The first one that we want to look at is `rtdev-build-releases.sh`. If
|
|
|
|
left unchanged, this script is going to do the following:
|
|
|
|
|
|
|
|
1. Download the source for the past three major Riak versions (e.g.
|
2013-01-23 16:43:19 +00:00
|
|
|
1.0.3, 1.1.4, and 1.2.1)
|
2012-09-17 21:16:59 +00:00
|
|
|
1. Build the proper version of Erlang that release was built with,
|
|
|
|
using kerl (which it will also download)
|
2012-08-22 15:39:34 +00:00
|
|
|
1. Build those releases of Riak.
|
|
|
|
|
2012-09-17 21:16:59 +00:00
|
|
|
You'll want to run this script from an empty directory. Also, you
|
|
|
|
might be thinking that you already have all the required versions of
|
|
|
|
erlang. Great! You can crack open the script and set the paths to your
|
|
|
|
installation:
|
2012-08-22 15:39:34 +00:00
|
|
|
|
|
|
|
```bash
|
|
|
|
R14B04=${R14B04:-$HOME/erlang-R14B04}
|
2013-01-23 16:43:19 +00:00
|
|
|
R15B03=${R15B03:-$HOME/erlang-R15B03}
|
2012-08-22 15:39:34 +00:00
|
|
|
```
|
|
|
|
|
2012-09-17 21:16:59 +00:00
|
|
|
**Kerlveat**: If you want kerl to build erlangs with serious 64-bit
|
|
|
|
macintosh action, you'll need a `~/.kerlrc` file that looks like this:
|
2012-08-22 16:42:35 +00:00
|
|
|
|
|
|
|
```
|
2012-09-10 19:56:28 +00:00
|
|
|
KERL_CONFIGURE_OPTIONS="--disable-hipe --enable-smp-support --enable-threads --enable-kernel-poll --enable-darwin-64bit"
|
2012-08-22 16:42:35 +00:00
|
|
|
```
|
|
|
|
|
2012-09-17 21:16:59 +00:00
|
|
|
The script will check that all these paths exist. If even one of them
|
|
|
|
is missing, it will prompt you to install kerl, even if you already
|
|
|
|
have kerl. If you say no, the script quits. If you say yes, or all of
|
|
|
|
your erlang paths check out, then go get a cup of coffee, you'll be
|
|
|
|
building for a little while.
|
2012-08-22 15:39:34 +00:00
|
|
|
|
2012-09-17 21:16:59 +00:00
|
|
|
**Warning**: If you are running OS X 10.7+ and trying to build Riak
|
|
|
|
1.0.3, then the erlang_js dependency won't compile for you, but it
|
|
|
|
fails silently. Fortunately, the precomipled OS X build includes this
|
|
|
|
dependency in it's working form. Just run `rtdev-lion-fix.sh` after
|
|
|
|
`rtdev-build-releases.sh` to patch it. **Please run this patch before
|
|
|
|
proceeding on to the next script**
|
2012-08-22 15:39:34 +00:00
|
|
|
|
|
|
|
### rtdev-setup-releases.sh
|
2012-09-17 21:16:59 +00:00
|
|
|
|
|
|
|
The `rtdev-setup-releases.sh` will get the releases you just built
|
|
|
|
into a local git repository. Currently, running this script from the
|
|
|
|
same directory that you just built all of your releases into.
|
2013-01-23 16:43:19 +00:00
|
|
|
Currently this script initializes this repository into `$HOME/rt/riak` but
|
2012-09-17 21:16:59 +00:00
|
|
|
it's probably worth making that configurable in the near term.
|
2012-08-22 16:07:16 +00:00
|
|
|
|
|
|
|
### rtdev-current.sh
|
2012-09-17 21:16:59 +00:00
|
|
|
|
|
|
|
`rtdev-current.sh` is where it gets interesting. You need to run that
|
|
|
|
from the Riak source folder you're wanting to test as the current
|
|
|
|
version of Riak. Also, make sure that you've already run `make devrel`
|
|
|
|
or `make stagedevrel` before you run `rtdev-current.sh`.
|
2012-08-22 16:07:16 +00:00
|
|
|
|
|
|
|
### Config file.
|
2012-09-17 21:16:59 +00:00
|
|
|
|
|
|
|
Now that you've got your releases all ready and gitified, you'll need
|
|
|
|
to tell riak_test about them. The method of choice is to create a
|
|
|
|
`~/.riak_test.config` that looks something like this:
|
2012-08-22 16:07:16 +00:00
|
|
|
|
|
|
|
```erlang
|
2012-10-17 14:55:52 +00:00
|
|
|
{default, [
|
2013-01-23 18:50:43 +00:00
|
|
|
{giddyup_host, "localhost:5000"},
|
|
|
|
{giddyup_user, "user"},
|
|
|
|
{giddyup_password, "password"},
|
|
|
|
{rt_max_wait_time, 600000},
|
|
|
|
{rt_retry_delay, 1000},
|
|
|
|
{rt_harness, rtdev},
|
|
|
|
{rt_scratch_dir, "/tmp/riak_test_scratch"},
|
|
|
|
{basho_bench, "~/basho/basho_bench"},
|
|
|
|
{spam_dir, "~/basho/riak_test/search-corpus/spam.0"},
|
|
|
|
{platform, "osx-64"}
|
2012-10-17 14:55:52 +00:00
|
|
|
]}.
|
|
|
|
|
|
|
|
{rtdev, [
|
2013-01-23 18:50:43 +00:00
|
|
|
{rt_project, "riak"},
|
|
|
|
{rtdev_path, [{root, "~/rt/riak"},
|
|
|
|
{current, "~/rt/riak/current"},
|
|
|
|
{previous, "~/rt/riak/riak-1.2.1"},
|
|
|
|
{legacy, "~/rt/riak/riak-1.1.4"}
|
|
|
|
]}
|
2012-08-22 16:07:16 +00:00
|
|
|
]}.
|
|
|
|
```
|
|
|
|
|
2012-10-17 14:55:52 +00:00
|
|
|
The `default` section of the config file will be overridden by the config
|
|
|
|
name you specify. For example, running the command below will use an
|
|
|
|
`rt_retry_delay` of 500 and an `rt_max_wait_time` of 180000. If your
|
|
|
|
defaults contain every option you need, you can run riak_test without
|
|
|
|
the `-c` argument.
|
|
|
|
|
2013-02-01 22:23:14 +00:00
|
|
|
Some configuration parameters:
|
|
|
|
|
|
|
|
#### rt_default_config
|
|
|
|
Default configuration parameters that will be used for nodes deployed by riak_test. Tests can
|
|
|
|
override these.
|
|
|
|
|
|
|
|
```erlang
|
|
|
|
{rtdev, [
|
|
|
|
{ rt_default_config,
|
|
|
|
[ {riak_core, [ {ring_creation_size, 16} ]} ] }
|
|
|
|
]}
|
|
|
|
```
|
|
|
|
|
2012-08-22 16:07:16 +00:00
|
|
|
### Running riak_test for the first time
|
2012-09-17 21:16:59 +00:00
|
|
|
|
2012-10-17 14:55:52 +00:00
|
|
|
Run a test! `./riak_test -c rtdev -t verify_build_cluster`
|
2012-08-22 16:07:16 +00:00
|
|
|
|
2012-09-17 21:16:59 +00:00
|
|
|
Did that work? Great, try something harder: `./riak_test -c
|
|
|
|
rtdev_mixed -t upgrade`
|
2012-11-13 21:19:50 +00:00
|
|
|
|
|
|
|
|
|
|
|
Intercepts
|
|
|
|
----------
|
|
|
|
|
|
|
|
Intercepts are a powerful but easy to wield feature. They allow you
|
|
|
|
to change the behavior of any function and affect global state in an
|
|
|
|
extremely lightweight manner. You can modify
|
|
|
|
[the KV vnode to simulate dropped puts][dropped_puts]. You can
|
|
|
|
[sleep a call][hashtree_sleep] to discover what happens when certain
|
|
|
|
calls take a long time to finish. You can even
|
|
|
|
[turn a call into a noop][ring_noop] to really cause havoc on a
|
|
|
|
cluster. These are just some examples. You should also be able to
|
|
|
|
change any function you want, including dependency functions and even
|
|
|
|
Erlang functions. Furthermore, any state you can reach from a
|
|
|
|
function call can be affected such as function arguments but also ETS
|
|
|
|
tables. This leads to the principle of intercepts.
|
|
|
|
|
|
|
|
> If you can do it in Riak source code you can do it with an
|
|
|
|
> intercept.
|
|
|
|
|
2012-12-20 21:43:03 +00:00
|
|
|
[dropped_puts]: https://github.com/basho/riak_test/blob/d284dcfc22d5d84ad301804691b16dbda6d91aa8/intercepts/riak_kv_vnode_intercepts.erl#L7
|
|
|
|
|
|
|
|
[hashtree_sleep]: https://github.com/basho/riak_test/blob/d284dcfc22d5d84ad301804691b16dbda6d91aa8/intercepts/hashtree_intercepts.erl#L5
|
|
|
|
|
|
|
|
[ring_noop]: https://github.com/basho/riak_test/blob/d284dcfc22d5d84ad301804691b16dbda6d91aa8/intercepts/riak_core_ring_manager_intercepts.erl#L5
|
|
|
|
|
2012-11-13 21:19:50 +00:00
|
|
|
### Writing Intercepts
|
|
|
|
|
|
|
|
Writing an intercept is nearly identical to writing any other Erlang
|
|
|
|
source with a few easy to remember conventions added.
|
|
|
|
|
|
|
|
1. All intercepts must live under the `intercepts` dir.
|
|
|
|
|
|
|
|
2. All intercept modules should be named the same as the module they
|
|
|
|
affect with the suffix `_intercepts` added. E.g. `riak_kv_vnode` =>
|
|
|
|
`riak_kv_vnode_intercepts`.
|
|
|
|
|
|
|
|
3. All intercept modules should include the `intercept.hrl` file.
|
|
|
|
This includes macros to properly log messages. You **cannot** call
|
|
|
|
lager.
|
|
|
|
|
|
|
|
4. All intercept modules should declare the macro `M` who's value is
|
|
|
|
the affected module with the suffix `_orig` added. E.g. for
|
|
|
|
`riak_kv_vnode` add the line `-define(M, riak_kv_vnode_orig)`.
|
|
|
|
This, along with the next convention is needed to call into the
|
|
|
|
original function.
|
|
|
|
|
|
|
|
5. To call the origin function use the `?M:` follow by the name of the
|
|
|
|
function with the `_orig` suffix appended. E.g. to call
|
|
|
|
`riak_kv_vnode:put` you would type `?M:put_orig`.
|
|
|
|
|
|
|
|
6. To log a message use the `I_` macros. E.g. to log an info message
|
|
|
|
use `?I_INFO`.
|
|
|
|
|
|
|
|
The easiest way to understand the above conventions is to see them all
|
|
|
|
at work in an example.
|
|
|
|
|
|
|
|
```erlang
|
|
|
|
-module(riak_kv_vnode_intercepts).
|
|
|
|
-compile(export_all).
|
|
|
|
-include("intercept.hrl").
|
|
|
|
-define(M, riak_kv_vnode_orig).
|
|
|
|
|
|
|
|
dropped_put(Preflist, BKey, Obj, ReqId, StartTime, Options, Sender) ->
|
|
|
|
NewPreflist = lists:sublist(Preflist, length(Preflist) - 1),
|
|
|
|
?I_INFO("Preflist modified from ~p to ~p", [Preflist, NewPreflist]),
|
|
|
|
?M:put_orig(NewPreflist, BKey, Obj, ReqId, StartTime, Options, Sender).
|
|
|
|
```
|
|
|
|
|
|
|
|
### How Do I Use Intercepts?
|
|
|
|
|
|
|
|
Intercepts can be used in two ways: 1) added via the config, 2) added
|
|
|
|
via `rpc:call` in the test. The first way is most convenient, is
|
|
|
|
persistent (survives node restarts), and is in effect for all tests.
|
|
|
|
The second method requires additional code, is specific to a test, is
|
|
|
|
ephemeral (does not survive a node restart), but allows more fine
|
|
|
|
grained control.
|
|
|
|
|
|
|
|
In both cases intercepts can be disabled by adding the following to
|
|
|
|
your config. By default intercepts will be loaded and compiled, but
|
|
|
|
not added. That is, they will be available but not in effect unless
|
|
|
|
you add them via one of the methods listed previously.
|
|
|
|
|
|
|
|
{load_intercepts, false}
|
|
|
|
|
|
|
|
#### Config
|
|
|
|
|
|
|
|
Here is how you would add the `dropped_put` intercept via the config.
|
|
|
|
|
2012-12-20 21:43:03 +00:00
|
|
|
{intercepts, [{riak_kv_vnode, [{{put,7}, dropped_put}]}]}
|
2012-11-13 21:19:50 +00:00
|
|
|
|
|
|
|
Breaking this down, the config key is `intercepts` and its value is a
|
|
|
|
list of intercepts to add. Each intercept definition in the list
|
|
|
|
describes which functions to intercept and what functions to intercept
|
|
|
|
them with. The example above would result in all calls to
|
|
|
|
`riak_kv_vnode:put/7` being intercepted by
|
|
|
|
`riak_kv_vnode_intercepts:dropped_put/7`.
|
|
|
|
|
2012-12-20 21:43:03 +00:00
|
|
|
{ModuleToIntercept, [{{FunctionToIntercept, Arity}, InterceptFunction}]}
|
2012-11-13 21:19:50 +00:00
|
|
|
|
|
|
|
#### Manual
|
|
|
|
|
|
|
|
To add the `dropped_put` intercept manually you would do the following.
|
|
|
|
|
2012-12-20 21:43:03 +00:00
|
|
|
`rt_intercept:add(Node, {riak_kv_vnode, [{{put,7}, dropped_put}]})`
|
2012-11-13 21:19:50 +00:00
|
|
|
|
|
|
|
### How Does it Work?
|
|
|
|
|
|
|
|
Knowing the implementation details is not needed to use intercepts but
|
|
|
|
this knowledge could come in handy if problems are encountered. There
|
|
|
|
are two parts to understand: 1) how the intercept code works and 2)
|
|
|
|
how intercepts are applied on-the-fly in Riak Test. It's important to
|
|
|
|
keep one thing in mind.
|
|
|
|
|
|
|
|
> Intercepts are based entirely on code generation and hot-swapping.
|
|
|
|
> The overhead of an intercept is always 1 or 2 function calls. 1 if
|
|
|
|
> a function is not being intercepted, 2 if it is and you call the
|
|
|
|
> original function.
|
|
|
|
|
|
|
|
The intercept code turns your original module into three. Based on
|
|
|
|
the mapping passed to `intercept:add` code is generated to re-route
|
|
|
|
requests to your intercept code or forward them to the original code.
|
|
|
|
E.g. if defining intercepts on `riak_kv_vnode` the following modules
|
|
|
|
will exist.
|
|
|
|
|
|
|
|
* `riak_kv_vnode_orig` - Contains the original code from
|
|
|
|
`riak_kv_vnode` but modified so that all original functions have the
|
|
|
|
suffix `_orig` added to them and the original function definitions
|
|
|
|
become passthrus to `riak_kv_vnode`, the proxy.
|
|
|
|
|
|
|
|
* `riak_kv_vnode_intercepts` - This contains code of your intercept as
|
|
|
|
you defined it. No modification of the code is performed.
|
|
|
|
|
|
|
|
* `riak_kv_vnode` - What once contained the original code is now a
|
|
|
|
proxy. All functions passthru to `riak_kv_vnode_orig`. Unless an
|
|
|
|
intercept is registered in the mapping passed to `intercept:add`.
|
|
|
|
In that case the call will forward to `riak_kv_vnode_intercepts`.
|
|
|
|
|
|
|
|
The interceptor code also modifies the original module and proxy to
|
|
|
|
export all functions. This fact, along with the fact that all the
|
|
|
|
original functions in `riak_kv_vnode_orig` will callback into the
|
|
|
|
proxy, means that you can intercept private functions as well.
|
|
|
|
|
|
|
|
In order for Riak Test to use intercepts they need to be compiled,
|
|
|
|
loaded, and registered on the nodes under test. You can't use the
|
|
|
|
bytecode generated by Riak Tests' rebar because the Erlang version
|
|
|
|
used will often be different from that included with your Riak nodes.
|
|
|
|
You could require that the user compile with the oldest Erlang version
|
|
|
|
supported but that is extra burden on the user and still doesn't
|
|
|
|
guarantee things will work if there is a jump of more than 2 majors in
|
|
|
|
Erlang version. No, this should be easy to use and thus the intercept
|
|
|
|
code is compiled **on** the Riak nodes guaranteeing that the bytecode
|
|
|
|
will be compatible.
|
|
|
|
|
|
|
|
After the code is compiled and loaded the intercepts need to be added.
|
|
|
|
All intercepts defined in the user's `riak_test.config` will be added
|
|
|
|
automatically any time a node is started. Thus, intercepts defined in
|
|
|
|
the config survive restarts and are essentially always in play. A
|
|
|
|
user can also manually add an intercept by making an `rpc` call from
|
|
|
|
the test code to the remote node. This method is ephemeral and the
|
|
|
|
intercept will not survive restarts.
|