Skip to content

Commit

Permalink
Added test of a riak_core cluster
Browse files Browse the repository at this point in the history
* Test with several slave nodes in a cluster (configured in config/test.exs).
  To prevent running RiakCore on the master node (harmless, but clutters the logs),
  invoke with `mix test --no-start`
* Fixed a few warnings
* Updated riak_core.schema

Notes:
* Running this project on Elixir 1.6.5 with Erlang 20.3.6.
  I had to comment out "warnings_as_errors"
  in `deps/riak_ensemble/rebar.config` and in `deps/riak_core/rebar.config`
* This project uses riak_core_ng v3.0.9.
  Later riak_core_ng commits introduced gen_fsm_compat, which fails
  with newer Elixir+rebar3 because of "missing erl_vsn" issue:
  rebar_erl_vsn is a pre-compile hook in rebar.config of gen_fsm_compat
  and few other projects.
  Error seems to be caused by Mix not handling rebar3 hooks properly.
  See elixir-lang/elixir#7733
  and Kyorai/riak_core#23

  This issue is not specific to erl_vsn:
  for example, the forked https://github.com/gpad/cuttlefish (see mix.exs)
  differs from the official version only by rebar.config commenting out:
     % {provider_hooks, [{post, [{compile, {default, escriptize}}]}]}.
  • Loading branch information
pmenhart committed Jun 6, 2018
1 parent 33a232b commit 152d1fb
Show file tree
Hide file tree
Showing 9 changed files with 360 additions and 10 deletions.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
Example application for the talk at [NoSlidesConf][0]. This application is a sample application with some functionalities available on riak_core.

## Erlang, Elixir, Rebar3 version

On this branch I start to use [asdf][4] and as you can find [here](./.tool-versions) I compile everything with:

```
Expand All @@ -22,6 +23,23 @@ mix deps.get
mix compile
```

Run the unit test that executes the riak_core ring onseveral slave nodes in a cluster. Nodes are configured in `config/test.exs`. To prevent running RiakCore on the master node (which is harmless, but clutters the logs), invoke with:
```shell
mix test --no-start
```

### Comments on Erlang/Elixir compatibility
(pmenhart, 2018-06-05) Hack to make the project working with erlang 20.3.6 and elixir 1.6.5:
* After `mix deps.get`, I had to comment out "warnings_as_errors" in `deps/riak_ensemble/rebar.config` and in `deps/riak_core/rebar.config`.
* Note this project is using `{:riak_core, "~> 3.0.9", hex: :riak_core_ng}`. Later riak_core_ng commits introduced gen_fsm_compat, which fails
with newer Elixir+rebar3 because of "missing erl_vsn" issue: rebar_erl_vsn is a pre-compile hook in rebar.config of gen_fsm_compat and few other projects.
Error seems to be caused by Mix not handling rebar3 hooks properly. See e.g. https://github.com/elixir-lang/elixir/issues/7733 and https://github.com/Kyorai/riak_core/issues/23
* This issue is not specific to erl_vsn. For example, the forked https://github.com/gpad/cuttlefish (as used in mix.exs here) differs from the official version only by rebar.config commenting out:
```
% {provider_hooks, [{post, [{compile, {default, escriptize}}]}]}.
```


## How to start a single node
If you want run a single node you can execute in this way:

Expand Down
8 changes: 8 additions & 0 deletions config/test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use Mix.Config

config :no_slides,
nodes: [
{"[email protected]", 8198, 8199},
{"[email protected]", 8298, 8299},
{"[email protected]", 8398, 8399}
]
2 changes: 1 addition & 1 deletion lib/no_slides/get_fsm_supervisor.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ defmodule NoSlides.GetFsmSupervisor do
Supervisor.start_link(__MODULE__, [], [name: __MODULE__])
end

def init(arg) do
def init(_arg) do
children = [
worker(NoSlides.GetFsm, [], restart: :temporary)
]
Expand Down
5 changes: 0 additions & 5 deletions lib/no_slides/service.ex
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,6 @@ defmodule NoSlides.Service do
end
end

def ring_status() do
{:ok, ring} = :riak_core_ring_manager.get_my_ring
:riak_core_ring.pretty_print(ring, [:legend])
end

def keys do
req_id = NoSlides.CoverageFsmSupervisor.start_fsm(:keys)
wait_result(req_id)
Expand Down
2 changes: 1 addition & 1 deletion lib/no_slides/write_fsm_supervisor.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ defmodule NoSlides.WriteFsmSupervisor do
Supervisor.start_link(__MODULE__, [], [name: __MODULE__])
end

def init(arg) do
def init(_arg) do
children = [
worker(NoSlides.WriteFsm, [], restart: :temporary)
]
Expand Down
5 changes: 5 additions & 0 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ defmodule NoSlides.Mixfile do
[app: :no_slides,
version: "0.1.0",
elixir: "~> 1.3",
elixirc_paths: elixirc_paths(Mix.env),
build_embedded: Mix.env == :prod,
start_permanent: Mix.env == :prod,
deps: deps()]
Expand All @@ -18,6 +19,10 @@ defmodule NoSlides.Mixfile do
mod: {NoSlides, []}]
end

# Specifies which paths to compile per environment.
defp elixirc_paths(:test), do: ["lib", "test/support"]
defp elixirc_paths(_), do: ["lib"]

# Dependencies can be Hex packages:
#
# {:mydep, "~> 0.3.0"}
Expand Down
184 changes: 183 additions & 1 deletion priv/riak_core.schema
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
%%-*- mode: erlang -*-

%% @doc enable active anti-entropy subsystem
{mapping, "anti_entropy", "riak_core.anti_entropy", [
{datatype, {enum, [on, off, debug]}},
Expand Down Expand Up @@ -295,6 +296,157 @@ end
Size =< 1024
end}.

{mapping, "buckets.default.pr", "riak_core.default_bucket_props.pr", [
{default, "0"},
{level, advanced}
]}.

%% Cut and paste translation screams to be rewritten as a datatype, but that's a
%% "nice to have"
{translation,
"riak_core.default_bucket_props.pr",
fun(Conf) ->
Setting = cuttlefish:conf_get("buckets.default.pr", Conf),
case Setting of
"quorum" -> quorum;
"all" -> all;
X ->
try list_to_integer(Setting) of
Int -> Int
catch
E:R -> error
end
end
end
}.

{mapping, "buckets.default.r", "riak_core.default_bucket_props.r", [
{default, "quorum"},
{level, advanced}
]}.
{translation,
"riak_core.default_bucket_props.r",
fun(Conf) ->
Setting = cuttlefish:conf_get("buckets.default.r", Conf),
case Setting of
"quorum" -> quorum;
"all" -> all;
X ->
try list_to_integer(Setting) of
Int -> Int
catch
E:R -> error
end
end
end
}.

{mapping, "buckets.default.w", "riak_core.default_bucket_props.w", [
{default, "quorum"},
{level, advanced}
]}.
{translation,
"riak_core.default_bucket_props.w",
fun(Conf) ->
Setting = cuttlefish:conf_get("buckets.default.w", Conf),
case Setting of
"quorum" -> quorum;
"all" -> all;
X ->
try list_to_integer(Setting) of
Int -> Int
catch
E:R -> error
end
end
end
}.

{mapping, "buckets.default.pw", "riak_core.default_bucket_props.pw", [
{default, "0"},
{level, advanced}
]}.
{translation,
"riak_core.default_bucket_props.pw",
fun(Conf) ->
Setting = cuttlefish:conf_get("buckets.default.pw", Conf),
case Setting of
"quorum" -> quorum;
"all" -> all;
X ->
try list_to_integer(Setting) of
Int -> Int
catch
E:R -> error
end
end
end
}.

{mapping, "buckets.default.dw", "riak_core.default_bucket_props.dw", [
{default, "quorum"},
{level, advanced}
]}.
{translation,
"riak_core.default_bucket_props.dw",
fun(Conf) ->
Setting = cuttlefish:conf_get("buckets.default.dw", Conf),
case Setting of
"quorum" -> quorum;
"all" -> all;
X ->
try list_to_integer(Setting) of
Int -> Int
catch
E:R -> error
end
end
end
}.

{mapping, "buckets.default.rw", "riak_core.default_bucket_props.rw", [
{default, "quorum"},
{level, advanced}
]}.
{translation,
"riak_core.default_bucket_props.rw",
fun(Conf) ->
Setting = cuttlefish:conf_get("buckets.default.rw", Conf),
case Setting of
"quorum" -> quorum;
"all" -> all;
X ->
try list_to_integer(Setting) of
Int -> Int
catch
E:R -> error
end
end
end
}.

%% {mapping, "buckets.default.basic_quorum", "riak_core.default_bucket_props.basic_quorum", false},
%% {mapping, "buckets.default.notfound_ok", "riak_core.default_bucket_props.notfound_ok", true}

%% @doc whether or not siblings are allowed.
%% Note: See Vector Clocks for a discussion of sibling resolution.
{mapping, "buckets.default.siblings", "riak_core.default_bucket_props.allow_mult", [
{datatype, {enum, [on, off]}},
{default, on},
{level, advanced}
]}.

{translation,
"riak_core.default_bucket_props.allow_mult",
fun(Conf) ->
Setting = cuttlefish:conf_get("buckets.default.siblings", Conf),
case Setting of
on -> true;
off -> false;
_Default -> true
end
end}.

{validator, "ring_size^2", "not a power of 2",
fun(Size) ->
(Size band (Size-1) =:= 0)
Expand Down Expand Up @@ -364,7 +516,7 @@ end
"can't be a local ip",
fun(AddrString) ->
case inet_parse:address(AddrString) of
{ok, {127, _, _, _}} -> false;
{ok, {127, 0, _, _}} -> false;
{ok, _} -> true;
{error, _} -> false
end
Expand Down Expand Up @@ -427,6 +579,19 @@ end
{datatype, flag}
]}.

%% consistent on/off (in lieu of enabled/disabled, true/false)
{ translation,
"riak_core.dtrace_support",
fun(Conf) ->
Setting = cuttlefish:conf_get("dtrace", Conf),
case Setting of
on -> true;
off -> false;
_Default -> false
end
end
}.

%% @doc Platform-specific installation paths (substituted by rebar)
{mapping, "platform_bin_dir", "riak_core.platform_bin_dir", [
{datatype, directory},
Expand Down Expand Up @@ -545,3 +710,20 @@ end
lists:sort(lists:foldl(Fold, [],
cuttlefish_variable:filter_by_prefix(["cluster", "job"], Conf)))
end}.


%% @doc Some requests to the vnodes are handled by an asyncronous worker pool.
%% This parameter allows for tuning this pools behaviour when it comes dealing
%% with requests that are queued.
%% The default (fifo) will serve requests in the order they arrive at the worker
%% pool. The alternative is to serve the requests in the reverse order, dealing
%% with the most recent request first.
%% There are pro's and con's for both aproaches, it is best to test out what
%% works best for the desired characteristics.
%%
%% As a very rought rule of thumb:
%% - fifo will lead to lower extremes
%% - filo will lead to lower medians/mediums
{mapping, "worker.queue_strategy", "riak_core.queue_worker_strategy",
[{default, fifo},
{datatype, {enum, [fifo, filo]}}]}.
51 changes: 49 additions & 2 deletions test/no_slides_test.exs
Original file line number Diff line number Diff line change
@@ -1,8 +1,55 @@
defmodule NoSlidesTest do
use ExUnit.Case
alias NoSlides.RiakCluster

doctest NoSlides

test "the truth" do
assert 1 + 1 == 2
# "setup_all" is called once per module before any test runs
setup_all do
# start the RiakCore cluster
assert !Node.alive?()
nodeNames = RiakCluster.start_test_nodes()
assert Node.alive?()
IO.inspect Node.list

#IO.inspect NoSlides.Service.ring_status() # local, meaningless. Fails without RiakCore running, e.g. 'mix test --no-start'
IO.inspect RiakCluster.ring_status(hd(nodeNames)) # remote call
[nodes: nodeNames]
end


test "ping different nodes in a RiakCore cluster", context do
nodeNames = context.nodes
:pong = rc_command(hd(nodeNames), :ping)

for _n <- 1..100 do
i = :rand.uniform(length(nodeNames)) - 1 # index of node to use
:pong = rc_command(Enum.at(nodeNames, i), :ping, [:rand.uniform(100_000_000)])
end
end

test "try key-value pairs in a RiakCore cluster", context do
nodeNames = context.nodes
first_node = hd(nodeNames)


:ok = rc_command(first_node, :put, [:k1, :v1])
:ok = rc_command(first_node, :put, [:k2, :v2])
:ok = rc_command(first_node, :put, [:k3, :v3])

# get from any of the nodes
for node <- nodeNames do
:v1 = rc_command(node, :get, [:k1])
:v2 = rc_command(node, :get, [:k2])
:v3 = rc_command(node, :get, [:k3])
nil = rc_command(node, :get, [:k10])
end
end

defp rc_command(node, command) do rc_command(node, command, []) end

defp rc_command(node, command, args) do
:rpc.call(String.to_atom(node), NoSlides.Service, command, args)
end

end
Loading

0 comments on commit 152d1fb

Please sign in to comment.