├── .gitignore ├── Makefile ├── README.md ├── include └── nkdist.hrl ├── rebar ├── rebar.config ├── src ├── nkdist.app.src ├── nkdist.erl ├── nkdist_admin.erl ├── nkdist_app.erl ├── nkdist_coverage.erl ├── nkdist_gen_server.erl ├── nkdist_proc.erl ├── nkdist_proc_sample.erl ├── nkdist_sup.erl ├── nkdist_util.erl ├── nkdist_vnode.erl └── nkdist_vnode_worker.erl └── util ├── dev1.config ├── dev2.config ├── dev3.config ├── dev4.config ├── dev5.config ├── dev_vm.args ├── riak_core.schema ├── shell_app.config └── shell_vm.args /.gitignore: -------------------------------------------------------------------------------- 1 | .eunit 2 | deps 3 | *.o 4 | *.beam 5 | *.plt 6 | erl_crash.dump 7 | ebin 8 | data 9 | log 10 | .rebar 11 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | REPO ?= nkdist 2 | 3 | .PHONY: deps release 4 | 5 | all: deps compile 6 | 7 | compile: 8 | ./rebar compile 9 | 10 | cnodeps: 11 | ./rebar compile skip_deps=true 12 | 13 | deps: 14 | ./rebar get-deps 15 | find deps -name "rebar.config" | xargs perl -pi -e 's/lager, "2.0.3"/lager, ".*"/g' 16 | (cd deps/lager && git checkout 2.1.1) 17 | 18 | clean: 19 | ./rebar clean 20 | 21 | distclean: clean 22 | ./rebar delete-deps 23 | 24 | tests: compile eunit 25 | 26 | eunit: 27 | export ERL_FLAGS="-config test/app.config -args_file test/vm.args"; \ 28 | ./rebar eunit skip_deps=true 29 | 30 | shell: 31 | erl -config util/shell_app.config -args_file util/shell_vm.args -s nkdist_app 32 | 33 | 34 | docs: 35 | ./rebar skip_deps=true doc 36 | 37 | 38 | dev1: 39 | erl -config util/dev1.config -args_file util/dev_vm.args \ 40 | -name dev1@127.0.0.1 -s nkdist_app 41 | 42 | dev2: 43 | erl -config util/dev2.config -args_file util/dev_vm.args \ 44 | -name dev2@127.0.0.1 -s nkdist_app 45 | 46 | dev3: 47 | erl -config util/dev3.config -args_file util/dev_vm.args \ 48 | -name dev3@127.0.0.1 -s nkdist_app 49 | 50 | dev4: 51 | erl -config util/dev4.config -args_file util/dev_vm.args \ 52 | -name dev4@127.0.0.1 -s nkdist_app 53 | 54 | dev5: 55 | erl -config util/dev5.config -args_file util/dev_vm.args \ 56 | -name dev5@127.0.0.1 -s nkdist_app 57 | 58 | 59 | APPS = kernel stdlib sasl erts ssl tools os_mon runtime_tools crypto inets \ 60 | xmerl webtool snmp public_key mnesia eunit syntax_tools compiler 61 | COMBO_PLT = $(HOME)/.$(REPO)_combo_dialyzer_plt 62 | 63 | check_plt: 64 | dialyzer --check_plt --plt $(COMBO_PLT) --apps $(APPS) deps/*/ebin 65 | 66 | build_plt: 67 | dialyzer --build_plt --output_plt $(COMBO_PLT) --apps $(APPS) deps/*/ebin 68 | 69 | dialyzer: 70 | dialyzer -Wno_return --plt $(COMBO_PLT) ebin/nkdist*.beam #| \ 71 | # fgrep -v -f ./dialyzer.ignore-warnings 72 | 73 | cleanplt: 74 | @echo 75 | @echo "Are you sure? It takes about 1/2 hour to re-build." 76 | @echo Deleting $(COMBO_PLT) in 5 seconds. 77 | @echo 78 | sleep 5 79 | rm $(COMBO_PLT) 80 | 81 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | NkDIST is an library to manage Erlang processes evenly distributed in a [_riak_core_](https://github.com/basho/riak_core) cluster. When you add or remove nodes from the cluster, NkDIST-based processes automatically _move_ to another node to rebalance the cluster. 4 | 5 | Before starting processes, you must supply one or several callback modules, using the [nkdist_proc](src/nkdist_proc.erl) behaviour. You must implement the following callbacks: 6 | 7 | Name|Desc 8 | ---|--- 9 | start/2|Called when the start of a new process has been requested for this node. You must start a new Erlang process and return its `pid()`. 10 | start_and_join/2|Called when an existing process must be moved from an old node to a new node. You receive the `pid()` of the old (still running) process and must start a new process and recover its state from the old one. 11 | join/2|Called when an existing process must be moved from an old node to a new node, but the process already exists in the new node. You must _join_ them. 12 | 13 | This would be a very simple example of a callback module. See the included [nkdist_proc_sample.erl](src/nkdist_proc_sample.erl) for a more complete version: 14 | 15 | ```erlang 16 | -module(sample). 17 | -behaviour(gen_server). 18 | -behaviour(nkdist_proc). 19 | 20 | -export([start/2, start_and_join/2, join/2]). 21 | -export([init/1, terminate/2, code_change/3, handle_call/3, 22 | handle_cast/2, handle_info/2]). 23 | 24 | 25 | %% nkdist_proc behaviour 26 | 27 | start(ProcId, Args) -> 28 | gen_server:start_link(?MODULE, {proc_id, ProcId, Args}, []). 29 | 30 | start_and_join(ProcId, OldPid) -> 31 | gen_server:start_link(?MODULE, {join, ProcId, OldPid}, []). 32 | 33 | join(Pid, OldPid) -> 34 | gen_server:call(Pid, {join, OldPid}). 35 | 36 | 37 | %% gen_server behaviour 38 | 39 | init({proc_id, _ProcId, Data}) -> 40 | {ok, Data}; 41 | init({join, _ProcId, OldPid}) -> 42 | case gen_server:call(OldPid, freeze) of 43 | {ok, Data} -> 44 | {ok, Data} 45 | _ -> 46 | {stop, could_not_start} 47 | end. 48 | 49 | handle_call(freeze, _From, Data) -> 50 | {stop, normal, {ok, Data}, State}; 51 | 52 | handle_call({join, OldPid}, _From, Data) -> 53 | case gen_server:call(OldPid, freeze) of 54 | {ok, OldData} -> 55 | {reply, ok, max(OldData, Data)}; 56 | _ -> 57 | {reply, {error, could_not_join}, State} 58 | end. 59 | 60 | handle_cast(_Msg, Data) -> 61 | {noreply, Data}. 62 | 63 | handle_info(_Msg, State) -> 64 | {noreply, State}. 65 | 66 | code_change(_OldVsn, State, _Extra) -> 67 | {ok, State}. 68 | 69 | terminate(_Reason, _State) -> 70 | ok. 71 | ``` 72 | 73 | Now, you can go to any node of the _riak_core_ cluster and start an instance of our process: 74 | 75 | ```erlang 76 | > nkdist:start_proc(proc1, sample, 0) 77 | {ok, <...>} 78 | ``` 79 | 80 | NkDIST will find the corresponding _vnode index_ for this process (using _consistent hashing_ over the `proc_id()`, _proc1_ in this example) and will send a request to that vnode to start the process. The function `sample:start/2` will be called, and the returned `pid()` is replied to the caller and stored in a map in the vnode. If the process dies, it is removed from the vnode store. 81 | 82 | Later on, you can find the `pid()` for the process calling `nkdist:find_proc(proc1)`. Once a `pid()` is returned, it is cached at each node in a local ETS table. 83 | 84 | When you add or remove nodes to the _riak_core_ cluster, the _handoff process_ starts and some vnodes must move from the old node to a new node. Each moving vnode will look at its managed processes, and will call `sample:start_and_join/2` at the new node, with the `pid()` of the old process. The callback function must start a new process, get any state from the old process and stop it as soon as possible. 85 | 86 | In some rare circumstances (like a network split) it can happen that a process with an specific `proc_id()` is started at two or more nodes at the same time. NkDIST is an _eventually consistent system_, so, once the network is reconnected, _handoffs_ will occur, and, if a node having a process with an specific `proc_id()` receives the _handoff_ from another with the same id, the callback `sample:join/2` would be called. The surviving process receiving the _join_ call must get any state from the old node and reconcile it with its own state, and stop the old node as soon as possible. 87 | 88 | 89 | ## Faillure of nodes 90 | 91 | If a node fails, all processes started at that node will be lost. You must save any important state in an external system (like [NkBASE](https://github.com/Nekso/nkbase)). 92 | 93 | Until the moment where the faillure is detected by the _Erlang distributed system_, requests for any vnode index assigned to that node will fail. Once it is detected, a _secondary vnode_ will be created at another node. New processes assigned to this vnode index will be started at the new, _temporary_ node. When the failed node comes back (or it is removed from the cluster), started processes will be moved or joined at the new, primary vnode. 94 | 95 | 96 | # Master management 97 | 98 | NkDIST has support for (eventually consistent) master election. You can use `nkdist:register/1` to register a process, belonging to a _class_ of processes. For each class, a _master_ will be elected, and the message `{nkcluster, Class, MasterPid}` will be sent to all registered processes. 99 | 100 | When calling `nkdist:register/1` you get the `pid()` of the corresponding _vnode_ so that you can monitor it, and re-register if it fails. 101 | 102 | 103 | ## NkDIST gen_master behaviour 104 | 105 | NkDIST includes a _gen_server-like_ behaviour, [`nkdist_gen_server`](src/nkdist_gen_server.erl). It is very similar to a standard OTP _gen_server_, but it elects a master among all started processes in the cluster with the same callback module. 106 | 107 | A new callback must be implemented, `handle_master/2`. It is called everytime a new (or the same) master is elected, with the `pid()` of the master, or `undefined` if no master is currently available (because of the temporary failure of the vnode). 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /include/nkdist.hrl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% 3 | %% Copyright (c) 2015 Carlos Gonzalez Florido. All Rights Reserved. 4 | %% 5 | %% This file is provided to you under the Apache License, 6 | %% Version 2.0 (the "License"); you may not use this file 7 | %% except in compliance with the License. You may obtain 8 | %% a copy of the License at 9 | %% 10 | %% http://www.apache.org/licenses/LICENSE-2.0 11 | %% 12 | %% Unless required by applicable law or agreed to in writing, 13 | %% software distributed under the License is distributed on an 14 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | %% KIND, either express or implied. See the License for the 16 | %% specific language governing permissions and limitations 17 | %% under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | 21 | -ifndef(NKDIST_HRL_). 22 | -define(NKDIST_HRL_, 1). 23 | 24 | %% =================================================================== 25 | %% Defines 26 | %% =================================================================== 27 | 28 | -define(APP, nkdist). 29 | -define(VMASTER, nkdist_vnode_master). 30 | 31 | 32 | %% =================================================================== 33 | %% Records 34 | %% =================================================================== 35 | 36 | 37 | 38 | 39 | -endif. 40 | 41 | -------------------------------------------------------------------------------- /rebar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetComposer/nkdist/515c7390def3c8839c6e40534105bb6f1fee00ff/rebar -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | % {lib_dirs, ["deps"]}. 2 | 3 | {erl_opts, [ 4 | % native, 5 | debug_info, 6 | fail_on_warning, 7 | {parse_transform, lager_transform} 8 | ]}. 9 | 10 | {cover_enabled, true}. 11 | {cover_export_enabled, true}. 12 | 13 | 14 | {deps, [ 15 | {riak_core, ".*", {git, "http://github.com/basho/riak_core", {tag, "2.1.2"}}}, 16 | {riak_dt, ".*", {git, "https://github.com/basho/riak_dt", {tag, "2.1.1"}}}, 17 | % {cluster_info, ".*", {git, "git://github.com/basho/cluster_info.git", {branch, "develop"}}}, 18 | {sext, ".*", {git, "https://github.com/uwiger/sext.git", {tag, "1.2"}}}, 19 | {nklib, ".*", {git, "http://github.com/nekso/nklib", {branch, "master"}}} 20 | ]}. 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/nkdist.app.src: -------------------------------------------------------------------------------- 1 | {application, nkdist, [ 2 | {description, "Nekso Distributed Library"}, 3 | {vsn, "master"}, 4 | {modules, []}, 5 | {registered, []}, 6 | {mod, {nkdist_app, []}}, 7 | {applications, [ 8 | kernel, 9 | stdlib, 10 | crypto, 11 | sasl, 12 | ssl, 13 | lager, 14 | nklib, 15 | riak_core 16 | ]}, 17 | {env, []} 18 | ]}. 19 | -------------------------------------------------------------------------------- /src/nkdist.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% 3 | %% Copyright (c) 2015 Carlos Gonzalez Florido. All Rights Reserved. 4 | %% 5 | %% This file is provided to you under the Apache License, 6 | %% Version 2.0 (the "License"); you may not use this file 7 | %% except in compliance with the License. You may obtain 8 | %% a copy of the License at 9 | %% 10 | %% http://www.apache.org/licenses/LICENSE-2.0 11 | %% 12 | %% Unless required by applicable law or agreed to in writing, 13 | %% software distributed under the License is distributed on an 14 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | %% KIND, either express or implied. See the License for the 16 | %% specific language governing permissions and limitations 17 | %% under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | 21 | %% @doc Main functions 22 | -module(nkdist). 23 | -author('Carlos Gonzalez '). 24 | 25 | -export([find_proc/2, find_proc_in_vnode/2, start_proc/3, get_procs/0, get_procs/1]). 26 | -export([register/1, get_masters/0, get_vnode/1, get_vnode/2]). 27 | -export([get_info/0]). 28 | 29 | -export_type([proc_id/0, vnode_id/0]). 30 | -include("nkdist.hrl"). 31 | 32 | 33 | %% =================================================================== 34 | %% Types 35 | %% =================================================================== 36 | 37 | -type proc_id() :: term(). 38 | -type vnode_id() :: {chash:index_as_int(), node()}. 39 | 40 | 41 | %% =================================================================== 42 | %% Public 43 | %% =================================================================== 44 | 45 | 46 | %% @doc Finds a process pid 47 | -spec find_proc(module(), proc_id()) -> 48 | {ok, pid()} | {error, not_found} | {error, term()}. 49 | 50 | find_proc(CallBack, ProcId) -> 51 | case nklib_proc:values({?APP, CallBack, ProcId}) of 52 | [{_, Pid}|_] -> 53 | {ok, Pid}; 54 | [] -> 55 | find_proc_in_vnode(CallBack, ProcId) 56 | end. 57 | 58 | 59 | %% @doc Finds a process pid directly in vnode 60 | %% (without using the cache) 61 | -spec find_proc_in_vnode(module(), proc_id()) -> 62 | {ok, pid()} | {error, not_found} | {error, term()}. 63 | 64 | find_proc_in_vnode(CallBack, ProcId) -> 65 | case get_vnode(CallBack, ProcId) of 66 | {ok, VNodeId} -> 67 | case nkdist_vnode:find_proc(VNodeId, CallBack, ProcId) of 68 | {ok, Pid} -> 69 | nklib_proc:put({?APP, CallBack, ProcId}, VNodeId, Pid), 70 | {ok, Pid}; 71 | {error, Error} -> 72 | {error, Error} 73 | end; 74 | error -> 75 | {error, no_vnode} 76 | end. 77 | 78 | 79 | %% @doc Starts a new process 80 | -spec start_proc(module(), proc_id(), term()) -> 81 | {ok, pid()} | {error, {already_started, pid()}} | {error, term()}. 82 | 83 | start_proc(CallBack, ProcId, Args) -> 84 | case get_vnode(CallBack, ProcId) of 85 | {ok, VNodeId} -> 86 | case nkdist_vnode:start_proc(VNodeId, CallBack, ProcId, Args) of 87 | {ok, Pid} -> 88 | nklib_proc:put({?APP, CallBack, ProcId}, VNodeId, Pid), 89 | {ok, Pid}; 90 | {error, {already_started, Pid}} -> 91 | nklib_proc:put({?APP, CallBack, ProcId}, VNodeId, Pid), 92 | {error, {already_started, Pid}}; 93 | {error, Error} -> 94 | {error, Error} 95 | end; 96 | error -> 97 | {error, no_vnode} 98 | end. 99 | 100 | 101 | %% @doc Gets all stared processes in the cluster 102 | -spec get_procs() -> 103 | {ok, [{{module(), proc_id()}, pid()}]}. 104 | 105 | get_procs() -> 106 | Fun = fun(Data, Acc) -> Data++Acc end, 107 | nkdist_coverage:launch(get_procs, 1, 10000, Fun, []). 108 | 109 | 110 | %% @doc Gets all stared processes in the cluster belonging to this callback 111 | -spec get_procs(module()) -> 112 | {ok, [{proc_id(), pid()}]}. 113 | 114 | get_procs(CallBack) -> 115 | Fun = fun(Data, Acc) -> Data++Acc end, 116 | nkdist_coverage:launch({get_procs, CallBack}, 1, 10000, Fun, []). 117 | 118 | 119 | %% @doc Registers a master class 120 | %% NkDIST will keep at a specific VNode the list of pids of all 121 | %% processes calling this function, and will send to all the message 122 | %% {nkdist_master, Class, pid()}, with the pid of the first 123 | %% successfully registered process. 124 | %% If this dies, the next one will be selected and sent to all. 125 | %% See nkdist_gen_server 126 | -spec register(atom()) -> 127 | {ok, VNode::pid()} | {error, term()}. 128 | 129 | register(Class) -> 130 | case get_vnode(Class) of 131 | {ok, VNodeId} -> 132 | nkdist_vnode:register(VNodeId, Class, self()); 133 | error -> 134 | {error, no_vnode} 135 | end. 136 | 137 | 138 | %% @doc Gets all stared processes in the cluster 139 | -spec get_masters() -> 140 | {ok, #{atom() => [pid()]}}. 141 | 142 | get_masters() -> 143 | Fun = fun(Map, Acc) -> maps:merge(Acc, Map) end, 144 | nkdist_coverage:launch(get_masters, 1, 10000, Fun, #{}). 145 | 146 | 147 | 148 | %% =================================================================== 149 | %% Private 150 | %% =================================================================== 151 | 152 | 153 | %% @private 154 | -spec get_vnode(term()) -> 155 | {ok, vnode_id()} | error. 156 | 157 | get_vnode(Term) -> 158 | get_vnode(undefined, Term). 159 | 160 | %% @private 161 | -spec get_vnode(module(), term()) -> 162 | {ok, vnode_id()} | error. 163 | 164 | get_vnode(Module, Term) -> 165 | DocIdx = case 166 | Module/=undefined andalso erlang:function_exported(Module, get_hash, 1) 167 | of 168 | true -> 169 | Module:get_hash(Term); 170 | false -> 171 | chash:key_of(Term) 172 | end, 173 | % We will get the associated IDX to this process, with a node that is 174 | % currently available. 175 | % If it is a secondary vnode (the node with the primary has failed), 176 | % a handoff process will move the process back to the primary 177 | case riak_core_apl:get_apl(DocIdx, 1, ?APP) of 178 | [{Idx, Node}] -> {ok, {Idx, Node}}; 179 | [] -> error 180 | end. 181 | 182 | 183 | %% @private 184 | get_info() -> 185 | Fun = fun(Map, Acc) -> [Map|Acc] end, 186 | case nkdist_coverage:launch(get_info, 1, 10000, Fun, []) of 187 | {ok, List} -> lists:sort(List); 188 | {error, Error} -> {error, Error} 189 | end. 190 | 191 | 192 | 193 | -------------------------------------------------------------------------------- /src/nkdist_admin.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% 3 | %% Copyright (c) 2015 Carlos Gonzalez Florido. All Rights Reserved. 4 | %% 5 | %% This file is provided to you under the Apache License, 6 | %% Version 2.0 (the "License"); you may not use this file 7 | %% except in compliance with the License. You may obtain 8 | %% a copy of the License at 9 | %% 10 | %% http://www.apache.org/licenses/LICENSE-2.0 11 | %% 12 | %% Unless required by applicable law or agreed to in writing, 13 | %% software distributed under the License is distributed on an 14 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | %% KIND, either express or implied. See the License for the 16 | %% specific language governing permissions and limitations 17 | %% under the License. 18 | %% 19 | %% Partially based on riak_kv_console and riak-admin 20 | %% 21 | %% ------------------------------------------------------------------- 22 | 23 | %% @doc Cluster Admin Module 24 | -module(nkdist_admin). 25 | -author('Carlos Gonzalez '). 26 | 27 | -export([get_info/0, print_info/0]). 28 | -export([join/1, leave/0, leave/1, force_remove/1, replace/2, force_replace/2]). 29 | -export([cluster_plan/0, cluster_commit/0, cluster_clear/0, cluster_status/0]). 30 | -export([partitions/0, partitions/1, partition_count/0, partition_count/1]). 31 | -export([partition_id/1, partition_index/1]). 32 | -export([ringready/0, down/1, transfers/0, transfer_limit/0]). 33 | -export([member_status/0, ring_status/0, cluster_info/1, stats/0, services/0]). 34 | -export([ticktime/1, reload_code/0]). 35 | -export([handoff_summary/0, handoff_enable/2, handoff_disable/2]). 36 | -export([handoff_details/0, handoff_details/1, handoff_config/0, handoff_config/1]). 37 | -export([stat_show/1, stat_info/1, stat_enable/1, stat_disable/1, stat_reset/1]). 38 | % -export([ensemble_overview/0, ensemble_detail/1]). 39 | 40 | % -include("../deps/riak_ensemble/include/riak_ensemble_types.hrl"). 41 | 42 | 43 | %% =================================================================== 44 | %% Summary 45 | %% =================================================================== 46 | 47 | get_info() -> 48 | [ 49 | {stats, riak_core_stat:get_stats()}, 50 | {ring_ready, riak_core_status:ringready()}, 51 | {all_active_transfers, riak_core_status:all_active_transfers()}, 52 | {transfers, riak_core_status:transfers()}, 53 | {vnodes, riak_core_vnode_manager:all_index_pid(nkdist_vnode)} 54 | ]. 55 | 56 | 57 | print_info() -> 58 | {ok, Ring} = riak_core_ring_manager:get_my_ring(), 59 | io:format("==================================================================\n", []), 60 | io:format("UP services: ~p\n", [riak_core_node_watcher:services()]), 61 | io:format("All members: ~p\n", [riak_core_ring:all_members(Ring)]), 62 | io:format("Active members: ~p\n", [riak_core_ring:active_members(Ring)]), 63 | io:format("Ready members: ~p\n", [riak_core_ring:ready_members(Ring)]), 64 | partitions(), 65 | % io:format("-------------------------- Idx2Num ------------------------------\n", []), 66 | % io:format("~p\n", [riak_core_mochiglobal:get(nkdist_idx2pos)]), 67 | OwnersData = riak_core_ring:all_owners(Ring), 68 | Owners = [{nkdist_util:idx2pos(Idx), Node} || {Idx, Node} <- OwnersData], 69 | io:format("--------------------------- Owners -------------------------------\n", []), 70 | io:format("~p\n", [Owners]), 71 | AllVNodes = 72 | [{Srv, nkdist_util:idx2pos(Idx), Pid} || 73 | {Srv, Idx, Pid} <- riak_core_vnode_manager:all_vnodes()], 74 | io:format("----------------------------- All VNodes -------------------------\n", []), 75 | io:format("~p\n", [lists:sort(AllVNodes)]), 76 | 77 | io:format("----------------------------- Transfers -------------------------\n", []), 78 | transfers(), 79 | 80 | riak_core_console:member_status([]), 81 | riak_core_console:ring_status([]), 82 | 83 | io:format("----------------------------- Handoff -------------------------\n", []), 84 | handoff_summary(), 85 | handoff_details(), 86 | % nkdist_ensemble_admin:ensemble_overview(), 87 | ok. 88 | 89 | 90 | %% =================================================================== 91 | %% Cluster commands 92 | %% 93 | %% The following commands stage changes to cluster membership. These commands 94 | %% do not take effect immediately. After staging a set of changes, the staged 95 | %% plan must be committed to take effect: 96 | %% 97 | %% join Join node to the cluster containing 98 | %% leave Have this node leave the cluster and shutdown 99 | %% leave Have leave the cluster and shutdown 100 | %% 101 | %% force-remove Remove from the cluster without 102 | %% first handing off data. Designed for 103 | %% crashed, unrecoverable nodes 104 | %% 105 | %% replace Have transfer all data to , 106 | %% and then leave the cluster and shutdown 107 | %% 108 | %% force-replace Reassign all partitions owned by to 109 | %% without first handing off data, and 110 | %% remove from the cluster. 111 | %% 112 | %% Staging commands: 113 | %% plan Display the staged changes to the cluster 114 | %% commit Commit the staged changes 115 | %% clear Clear the staged changes 116 | %% 117 | %% Status and information commands: 118 | %% status Display a concise summary of node membership 119 | %% availability and ring ownership. 120 | %% 121 | %% partitions [--node=] Print primary, secondary and stopped 122 | %% partition indices and ids for the current 123 | %% node, or for the specified node. 124 | %% 125 | %% partition-count [--node=] Print the cluster-wide number of 126 | %% partitions or the number of partitions 127 | %% on the specified node. 128 | %% 129 | %% partition id= Convert the given partition id to the 130 | %% matching index. 131 | %% 132 | %% partition index= Convert the given partition index to 133 | %% the matching id. 134 | %% =================================================================== 135 | 136 | %% @doc Prepares a new node for join 137 | -spec join(string()) -> ok | error. 138 | join(Node) when is_list(Node) -> 139 | try 140 | case riak_core:staged_join(Node) of 141 | ok -> 142 | io:format("Success: staged join request for ~p to ~p~n", 143 | [node(), Node]), 144 | ok; 145 | {error, not_reachable} -> 146 | io:format("Node ~s is not reachable!~n", [Node]), 147 | error; 148 | {error, different_ring_sizes} -> 149 | io:format("Failed: ~s has a different ring_creation_size~n", 150 | [Node]), 151 | error; 152 | {error, unable_to_get_join_ring} -> 153 | io:format("Failed: Unable to get ring from ~s~n", [Node]), 154 | error; 155 | {error, not_single_node} -> 156 | io:format("Failed: This node is already a member of a " 157 | "cluster~n"), 158 | error; 159 | {error, self_join} -> 160 | io:format("Failed: This node cannot join itself in a " 161 | "cluster~n"), 162 | error 163 | % {error, _} -> 164 | % io:format("Join failed. Try again in a few moments.~n", []), 165 | % error 166 | end 167 | catch 168 | Exception:Reason -> 169 | lager:error("Join failed ~p:~p", [Exception, Reason]), 170 | io:format("Join failed, see log for details~n"), 171 | error 172 | end. 173 | 174 | 175 | %% @doc Leaves the cluster (data partitions are handed off) 176 | -spec leave() -> ok | error. 177 | leave() -> 178 | riak_core_console:stage_leave([]). 179 | 180 | 181 | %% @doc Instructs a node to leave the cluster (data partitions are handed off) 182 | -spec leave(string()) -> ok | error. 183 | leave(Node) when is_list(Node) -> 184 | riak_core_console:stage_leave([Node]). 185 | 186 | 187 | %% @doc Removes another node from the cluster without first handing off 188 | %% its data partitions (for use wirh crashed or unrecoverable nodes). 189 | -spec force_remove(string()) -> ok | error. 190 | force_remove(Node) when is_list(Node) -> 191 | riak_core_console:stage_remove([Node]). 192 | 193 | 194 | %% @doc Transfer all node partitions of Node1 to Node2 195 | -spec replace(string(), string()) -> ok | error. 196 | replace(Node1, Node2) when is_list(Node1), is_list(Node2) -> 197 | riak_core_console:stage_replace([Node1, Node2]). 198 | 199 | 200 | %% @doc Transfer all node partitions of Node1 to Node2, without handing of data 201 | -spec force_replace(string(), string()) -> ok | error. 202 | force_replace(Node1, Node2) when is_list(Node1), is_list(Node2) -> 203 | riak_core_console:stage_force_replace([Node1, Node2]). 204 | 205 | 206 | % %% @doc Resize ring (NOT YET SUPPORTED) 207 | % -spec resize_ring(string()) ->ok | error. 208 | % resize_ring(Size) when is_list(Size) -> 209 | % riak_core_console:stage_resize_ring([Size]). 210 | 211 | 212 | %% @doc Prints the current cluster plan 213 | -spec cluster_plan() -> ok. 214 | cluster_plan() -> 215 | riak_core_console:print_staged([]). 216 | 217 | 218 | %% @doc Commits the current cluster plan 219 | -spec cluster_commit() -> ok | error. 220 | cluster_commit() -> 221 | riak_core_console:commit_staged([]). 222 | 223 | 224 | %% @doc Clears the current cluster plan 225 | -spec cluster_clear() -> ok. 226 | cluster_clear() -> 227 | riak_core_console:clear_staged([]). 228 | 229 | 230 | %% @doc Prints cluster status 231 | -spec cluster_status() -> ok. 232 | cluster_status() -> 233 | riak_core_console:command(["riak-admin", "cluster", "status"]). 234 | 235 | 236 | %% @doc Prints partition table 237 | -spec partitions() -> ok. 238 | partitions() -> 239 | riak_core_console:command(["riak-admin", "cluster", "partitions"]). 240 | 241 | 242 | %% @doc Prints partition table for a node 243 | -spec partitions(string()) -> ok. 244 | partitions(Node) when is_list(Node) -> 245 | riak_core_console:command(["riak-admin", "cluster", "partitions", "-n", Node]). 246 | 247 | 248 | %% @doc Prints partition count 249 | -spec partition_count() -> ok. 250 | partition_count() -> 251 | riak_core_console:command(["riak-admin", "cluster", "partition-count"]). 252 | 253 | 254 | %% @doc Prints partition count for a node 255 | -spec partition_count(string()) -> ok. 256 | partition_count(Node) when is_list(Node) -> 257 | riak_core_console:command(["riak-admin", "cluster", "partition-count", "-n", Node]). 258 | 259 | 260 | %% @doc Prints partition table for a node 261 | -spec partition_id(string()) -> ok. 262 | partition_id(Id) when is_list(Id) -> 263 | riak_core_console:command(["riak-admin", "cluster", "partition", "id="++Id]). 264 | 265 | 266 | %% @doc Prints partition table for a node 267 | -spec partition_index(string()) -> ok. 268 | partition_index(Id) when is_list(Id) -> 269 | riak_core_console:command(["riak-admin", "cluster", "partition", "index="++Id]). 270 | 271 | 272 | 273 | %% =================================================================== 274 | %% Other Cluster 275 | %% =================================================================== 276 | 277 | %% @doc Check if all nodes in the cluster agree on the partition assignment 278 | -spec ringready() -> ok | error. 279 | ringready() -> 280 | try 281 | case riak_core_status:ringready() of 282 | {ok, Nodes} -> 283 | io:format("TRUE All nodes agree on the ring ~p\n", [Nodes]); 284 | {error, {different_owners, N1, N2}} -> 285 | io:format("FALSE Node ~p and ~p list different partition owners\n", [N1, N2]), 286 | error; 287 | {error, {nodes_down, Down}} -> 288 | io:format("FALSE ~p down. All nodes need to be up to check.\n", [Down]), 289 | error 290 | end 291 | catch 292 | Exception:Reason -> 293 | lager:error("Ringready failed ~p:~p", [Exception, 294 | Reason]), 295 | io:format("Ringready failed, see log for details~n"), 296 | error 297 | end. 298 | 299 | 300 | %% @doc Marks a node as down so that ring transitions can be performed 301 | %% before the node is brought back online. 302 | -spec down(string()) -> ok | error. 303 | down(Node) -> 304 | try 305 | case riak_core:down(list_to_atom(Node)) of 306 | ok -> 307 | io:format("Success: ~p marked as down~n", [Node]), 308 | ok; 309 | {error, legacy_mode} -> 310 | io:format("Cluster is currently in legacy mode~n"), 311 | ok; 312 | {error, is_up} -> 313 | io:format("Failed: ~s is up~n", [Node]), 314 | error; 315 | {error, not_member} -> 316 | io:format("Failed: ~p is not a member of the cluster.~n", 317 | [Node]), 318 | error; 319 | {error, only_member} -> 320 | io:format("Failed: ~p is the only member.~n", [Node]), 321 | error 322 | end 323 | catch 324 | Exception:Reason -> 325 | lager:error("Down failed ~p:~p", [Exception, Reason]), 326 | io:format("Down failed, see log for details~n"), 327 | error 328 | end. 329 | 330 | 331 | %% @doc Provide a list of nodes with pending partition transfers 332 | %% (i.e. any secondary vnodes) and list any owned vnodes that are *not* running 333 | -spec transfers() -> ok. 334 | transfers() -> 335 | riak_core_console:transfers([]). 336 | 337 | 338 | %% @doc Print handoff transfer limit 339 | -spec transfer_limit() -> ok. 340 | transfer_limit() -> 341 | riak_core_console:transfer_limit([]). 342 | 343 | %% @doc Prints member status 344 | -spec member_status() -> ok. 345 | member_status() -> 346 | riak_core_console:member_status([]). 347 | 348 | 349 | %% @doc Prints ring status 350 | -spec ring_status() -> ok. 351 | ring_status() -> 352 | riak_core_console:ring_status([]). 353 | 354 | 355 | %% @doc Dumps cluster info to file(s) 356 | %% Format: `` ['local' | ['local' | ] [...]]'' 357 | -spec cluster_info([string()]) -> ok | error. 358 | cluster_info([OutFile|Rest]) -> 359 | try 360 | case lists:reverse(atomify_nodestrs(Rest)) of 361 | [] -> 362 | cluster_info:dump_all_connected(OutFile); 363 | Nodes -> 364 | cluster_info:dump_nodes(Nodes, OutFile) 365 | end 366 | catch 367 | error:{badmatch, {error, eacces}} -> 368 | io:format("Cluster_info failed, permission denied writing to ~p~n", [OutFile]); 369 | error:{badmatch, {error, enoent}} -> 370 | io:format("Cluster_info failed, no such directory ~p~n", [filename:dirname(OutFile)]); 371 | error:{badmatch, {error, enotdir}} -> 372 | io:format("Cluster_info failed, not a directory ~p~n", [filename:dirname(OutFile)]); 373 | Exception:Reason -> 374 | lager:error("Cluster_info failed ~p:~p", 375 | [Exception, Reason]), 376 | io:format("Cluster_info failed, see log for details~n"), 377 | error 378 | end. 379 | 380 | 381 | %% @doc Lists core stats 382 | -spec stats() -> [{atom(), term()}]. 383 | stats() -> 384 | riak_core_stat:get_stats(). 385 | 386 | 387 | %% @doc Lists available services 388 | -spec services() -> [atom()]. 389 | services() -> 390 | riak_core_node_watcher:services(). 391 | 392 | 393 | %% @doc Updates system ticktime (default is 60) 394 | -spec ticktime(integer()) -> ok. 395 | ticktime(Time) when is_integer(Time), Time > 1 -> 396 | riak_core_net_ticktime:start_set_net_ticktime_daemon(node(), Time). 397 | 398 | 399 | %% @doc Get new paths and reloads all code 400 | -spec reload_code() -> ok. 401 | reload_code() -> 402 | case app_helper:get_env(nkdist, add_paths) of 403 | List when is_list(List) -> 404 | _ = [ reload_path(filename:absname(Path)) || Path <- List ], 405 | ok; 406 | _ -> 407 | ok 408 | end. 409 | 410 | 411 | %% =================================================================== 412 | %% VNode Status 413 | %% =================================================================== 414 | 415 | % %% @doc Get Vnode Status 416 | % -spec vnode_status() -> term(). 417 | % vnode_status() -> 418 | % nkdist_cmds:send_all(status, #{}). 419 | 420 | 421 | 422 | %% =================================================================== 423 | %% Handoff 424 | %% =================================================================== 425 | 426 | %% @doc Get handoff summary 427 | -spec handoff_summary() -> ok. 428 | handoff_summary() -> 429 | riak_core_console:command(["riak-admin", "handoff", "summary"]). 430 | 431 | 432 | %% @doc Enables handoff 433 | -spec handoff_enable(inbound|outbound|both, string()) -> ok. 434 | handoff_enable(Type, Node) 435 | when (Type==inbound orelse Type==outbound orelse Type==both) 436 | andalso (Node==all orelse is_list(Node)) -> 437 | NodeStr = case Node of 438 | all -> ["-a"]; 439 | _ -> ["-n", Node] 440 | end, 441 | riak_core_console:command(["riak-admin", "handoff", "enable", 442 | atom_to_list(Type) | NodeStr]). 443 | 444 | 445 | %% @doc Disables handoff 446 | -spec handoff_disable(inbound|outbound|both, string()) -> ok. 447 | handoff_disable(Type, Node) 448 | when Type==inbound; Type==outbound; Type==both -> 449 | NodeStr = case Node of 450 | all -> ["-a"]; 451 | _ -> ["-n", Node] 452 | end, 453 | riak_core_console:command(["riak-admin", "handoff", "disable", 454 | atom_to_list(Type) | NodeStr]). 455 | 456 | 457 | %% @doc Print handoff details 458 | -spec handoff_details() -> ok. 459 | handoff_details() -> 460 | riak_core_console:command(["riak-admin", "handoff", "details", "-a"]). 461 | 462 | 463 | %% @doc Print handoff details for an node 464 | -spec handoff_details(string()) -> ok. 465 | handoff_details(Node) when is_list(Node) -> 466 | riak_core_console:command(["riak-admin", "handoff", "details", "-n", Node]). 467 | 468 | 469 | %% @doc Print handoff config 470 | -spec handoff_config() -> ok. 471 | handoff_config() -> 472 | riak_core_console:command(["riak-admin", "handoff", "config", "-a"]). 473 | 474 | 475 | %% @doc Print handoff config for a node 476 | -spec handoff_config(string()) -> ok. 477 | handoff_config(Node) when is_list(Node) -> 478 | riak_core_console:command(["riak-admin", "handoff", "config", "-n", Node]). 479 | 480 | 481 | 482 | %% =================================================================== 483 | %% Stats commands 484 | %% 485 | %% (copied info from riak-admin) 486 | %% The following commands display, enable/disable and reset statistics. 487 | %% A statistics entry is given either as a 'dotted' exometer name - 488 | %% Identifiers separated by periods, '.', e.g. riak.riak_kv.node.gets, 489 | %% or as a 'legacy' name (same as in riak-admin status) - e.g. node_gets. 490 | %% When a legacy name is listed, the corresponding exometer name is shown as well. 491 | %% 492 | %% Two kinds of wildcard are suppored: 493 | %% * - matches anything up to the next separator ('.' or '_') or end of name; 494 | %% ** - matches anything including separators. 495 | %% Quoting is permitted. 496 | %% =================================================================== 497 | 498 | 499 | %% @doc Show matching stats entries together with corresponding values 500 | %% 501 | %% 502 | %% The format of can be one of: 503 | %% - 'Dotted exometer name': In Exometer, entries are represented as [A,B,...]. 504 | %% These names can be emulated on the command-line as A.B.... Wildcards are 505 | %% supported: '*' will match anything between deliminators (dots), whereas 506 | %% '**' will match anything including deliminators. Thus \`stat show \"*.**\"\` 507 | %% will match all stats entries. All Riak stat entry names start with 'riak', 508 | %% so \`stat show riak.**\` will match all riak stat entries. 509 | %% 510 | %% Example: 511 | %% \$ bin/riak-admin stat show riak.riak_kv.node.gets 512 | %% [riak,riak_kv,node,gets]: [{count,0},{one,0}] 513 | %% 514 | %% - 'Legacy name': The stat names used e.g. in \`$SCRIPT status\` can be used 515 | %% here, but also with wildcard support. The corresponding Exometer name and 516 | %% datapoint will be shown as well. 517 | %% 518 | %% Example: 519 | %% \$ bin/riak-admin stat show node_gets 520 | %% == node_gets (Legacy pattern): == 521 | %% node_gets: 0 ([riak,riak_kv,node,gets]/one) 522 | %% 523 | %% (Note: A single '*' is treated as a legacy name and would match all such 524 | %% names that contain no underscores; to match all exometer names, a '.' must 525 | %% be present, so '*.**' would work as a catch-all expression.) 526 | %% 527 | %% Each Exometer entry has a type and a set of datapoints. A filter can be 528 | %% given on the command line, selecting only a subset of datapoints: 529 | %% 530 | %% \$ bin/riak-admin stat show riak.riak_kv.node.gets/one 531 | %% [riak,riak_kv,node,gets]: [{one,0}] 532 | %% 533 | %% The type can also be restricted: 534 | %% \$ bin/riak-admin stat show *.**/type=duration/mean,max 535 | %% [riak,riak_core,converge_delay]: [{mean,0},{max,0}] 536 | %% [riak,riak_core,rebalance_delay]: [{mean,0},{max,0}] 537 | %% 538 | %% Note how multiple datapoints are separated by comma (no space). 539 | %% 540 | %% Showing disabled entries: 541 | %% \$ bin/riak-admin stat show riak.riak_kv.node.gets 542 | %% No matching stats 543 | %% \$ bin/riak-admin stat show riak.riak_kv.node.gets/status=* 544 | %% [riak,riak_kv,node,gets]: disabled 545 | %% \$ bin/riak-admin stat show riak.riak_kv.node.gets/status=disabled 546 | %% [riak,riak_kv,node,gets]: disabled 547 | %% 548 | 549 | -spec stat_show(string()) -> ok. 550 | stat_show(String) -> 551 | riak_core_console:stat_show([String]). 552 | 553 | 554 | %% @doc Display Exometer meta-data for matching entries. 555 | %% Type of data can be controlled 556 | %% with options: 557 | %% 558 | %% info [ -name | -type 559 | %% | -module 560 | %% | -value | -cache 561 | %% | -status | -timestamp 562 | %% | -options | -ref 563 | %% | -datapoints ] 564 | %% 565 | %% The same entry formats can be used as for all other stat subcommands. 566 | %% 567 | %% Example: 568 | %% \$ bin/riak-admin stat info riak.riak_kv.node.gets 569 | %% [riak,riak_kv,node,gets]: name = [riak,riak_kv,node,gets] 570 | %% type = spiral 571 | %% module = exometer_spiral 572 | %% value = disabled 573 | %% cache = 0 574 | %% status = disabled 575 | %% timestamp = undefined 576 | %% options = [{status,disabled}] 577 | %% 578 | %% \$ bin/riak-admin stat info -type -status riak.riak_kv.node.gets 579 | %% [riak,riak_kv,node,gets]: type = spiral 580 | %% status = disabled 581 | 582 | -spec stat_info(string()) -> any(). 583 | stat_info(String) -> 584 | riak_core_console:stat_info([String]). 585 | 586 | 587 | %% @doc Enable statistics 588 | %% Exometer stats can be disabled and enabled. Disabled entries are not actively 589 | %% updated, and have no value. 590 | %% 591 | %% The same syntax can be used as in \`stat show\`. The requested action will be 592 | %% performed on the matching entries. 593 | %% 594 | %% \$ bin/riak-admin stat disable node_gets 595 | %% == node_gets (Legacy pattern): == 596 | %% [riak,riak_kv,node,gets]: disabled 597 | %% \$ bin/riak-admin stat enable node_gets 598 | %% == node_gets (Legacy pattern): == 599 | %% [riak,riak_kv,node,gets]: enabled 600 | %% 601 | %% Wildcards can be used: 602 | %% 603 | %% \$ bin/riak-admin stat disable riak.riak_kv.node.* 604 | %% [riak,riak_kv,node,gets]: disabled 605 | %% [riak,riak_kv,node,puts]: disabled 606 | 607 | -spec stat_enable(string()) -> ok. 608 | stat_enable(String) -> 609 | riak_core_console:stat_enable([String]). 610 | 611 | 612 | %% @doc Disable statistics 613 | -spec stat_disable(string()) -> ok. 614 | stat_disable(String) -> 615 | riak_core_console:stat_disable([String]). 616 | 617 | 618 | %% @doc Rest statistics. "**" for all. 619 | %% Reset matching stat entries. Only enabled entries can be reset. 620 | 621 | -spec stat_reset(string()) -> ok. 622 | stat_reset(String) -> 623 | riak_core_console:stat_reset([String]). 624 | 625 | 626 | %% =================================================================== 627 | %% Ensemble 628 | %% =================================================================== 629 | 630 | 631 | % ensemble_overview() -> 632 | % nkdist_ensemble_admin:ensemble_overview(). 633 | 634 | 635 | % ensemble_detail(N) -> 636 | % nkdist_ensemble_admin:ensemble_detail(N). 637 | 638 | 639 | 640 | 641 | 642 | %% =================================================================== 643 | %% Internal 644 | %% =================================================================== 645 | 646 | atomify_nodestrs(Strs) -> 647 | lists:foldl(fun("local", Acc) -> [node()|Acc]; 648 | (NodeStr, Acc) -> try 649 | [list_to_existing_atom(NodeStr)|Acc] 650 | catch error:badarg -> 651 | io:format("Bad node: ~s\n", [NodeStr]), 652 | Acc 653 | end 654 | end, [], Strs). 655 | 656 | reload_path(Path) -> 657 | {ok, Beams} = file:list_dir(Path), 658 | [ reload_file(filename:absname(Beam, Path)) || Beam <- Beams, ".beam" == filename:extension(Beam) ]. 659 | 660 | reload_file(Filename) -> 661 | Mod = list_to_atom(filename:basename(Filename, ".beam")), 662 | case code:is_loaded(Mod) of 663 | {file, Filename} -> 664 | code:soft_purge(Mod), 665 | {module, Mod} = code:load_file(Mod), 666 | io:format("Reloaded module ~w from ~s.~n", [Mod, Filename]); 667 | {file, Other} -> 668 | io:format("CONFLICT: Module ~w originally loaded from ~s, won't reload from ~s.~n", [Mod, Other, Filename]); 669 | _ -> 670 | io:format("Module ~w not yet loaded, skipped.~n", [Mod]) 671 | end. 672 | 673 | -------------------------------------------------------------------------------- /src/nkdist_app.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% 3 | %% Copyright (c) 2015 Carlos Gonzalez Florido. All Rights Reserved. 4 | %% 5 | %% This file is provided to you under the Apache License, 6 | %% Version 2.0 (the "License"); you may not use this file 7 | %% except in compliance with the License. You may obtain 8 | %% a copy of the License at 9 | %% 10 | %% http://www.apache.org/licenses/LICENSE-2.0 11 | %% 12 | %% Unless required by applicable law or agreed to in writing, 13 | %% software distributed under the License is distributed on an 14 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | %% KIND, either express or implied. See the License for the 16 | %% specific language governing permissions and limitations 17 | %% under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | 21 | %% @doc NkDIST LIB OTP Application Module 22 | -module(nkdist_app). 23 | -author('Carlos Gonzalez '). 24 | -behaviour(application). 25 | 26 | -export([start/0, start/1, start/2, stop/1]). 27 | -export([get/1, put/2]). 28 | 29 | -include("nkdist.hrl"). 30 | % -include_lib("nklib/include/nklib.hrl"). 31 | 32 | -compile({no_auto_import, [get/1, put/2]}). 33 | 34 | %% =================================================================== 35 | %% Private 36 | %% =================================================================== 37 | 38 | %% @doc Starts stand alone. 39 | -spec start() -> 40 | ok | {error, Reason::term()}. 41 | 42 | start() -> 43 | start(temporary). 44 | 45 | 46 | %% @doc Starts stand alone. 47 | -spec start(permanent | transient | temporary) -> 48 | ok | {error, Reason::term()}. 49 | 50 | start(Type) -> 51 | nkdist_util:ensure_dir(), 52 | case nklib_util:ensure_all_started(?APP, Type) of 53 | {ok, _Started} -> 54 | riak_core:wait_for_service(nkdist), 55 | ok; 56 | Error -> 57 | Error 58 | end. 59 | 60 | 61 | %% @private OTP standard start callback 62 | start(_Type, _Args) -> 63 | {ok, Vsn} = application:get_key(?APP, vsn), 64 | nkdist_util:store_idx_cache(), 65 | lager:notice("NkDIST v~s is starting", [Vsn]), 66 | {ok, Pid} = nkdist_sup:start_link(), 67 | riak_core:register(?APP, [{vnode_module, nkdist_vnode}]), 68 | %% Force the creation of vnodes before waiting for 69 | %% 'vnode_management_timer' time 70 | {ok, Ring} = riak_core_ring_manager:get_my_ring(), 71 | riak_core_ring_handler:ensure_vnodes_started(Ring), 72 | {ok, Pid}. 73 | 74 | 75 | %% @private OTP standard stop callback 76 | stop(_) -> 77 | ok. 78 | 79 | 80 | %% @doc gets a configuration value 81 | get(Key) -> 82 | get(Key, undefined). 83 | 84 | 85 | %% @doc gets a configuration value 86 | get(Key, Default) -> 87 | nklib_config:get(?APP, Key, Default). 88 | 89 | 90 | %% @doc updates a configuration value 91 | put(Key, Value) -> 92 | nklib_config:put(?APP, Key, Value). 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /src/nkdist_coverage.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% 3 | %% Copyright (c) 2015 Carlos Gonzalez Florido. All Rights Reserved. 4 | %% 5 | %% This file is provided to you under the Apache License, 6 | %% Version 2.0 (the "License"); you may not use this file 7 | %% except in compliance with the License. You may obtain 8 | %% a copy of the License at 9 | %% 10 | %% http://www.apache.org/licenses/LICENSE-2.0 11 | %% 12 | %% Unless required by applicable law or agreed to in writing, 13 | %% software distributed under the License is distributed on an 14 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | %% KIND, either express or implied. See the License for the 16 | %% specific language governing permissions and limitations 17 | %% under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | 21 | %% @private Coverage management module 22 | -module(nkdist_coverage). 23 | -author('Carlos Gonzalez '). 24 | 25 | -behavior(riak_core_coverage_fsm). 26 | 27 | -include("nkdist.hrl"). 28 | 29 | -export([launch/5]). 30 | -export([init/2, process_results/2, finish/2]). 31 | 32 | 33 | 34 | %% @doc Launches a new coverage job 35 | -spec launch(term(), pos_integer(), pos_integer(), 36 | fun((Data::term(), Acc::term()) -> NewAcc::term()), Acc0::term()) -> 37 | {ok, MSecs::integer(), term()} | {error, term()}. 38 | 39 | launch(Cmd, N, Timeout, FoldFun, FoldAcc) -> 40 | ReqId = erlang:phash2(make_ref()), 41 | From = {raw, ReqId, self()}, 42 | {ok, _} = supervisor:start_child(nkdist_coverage_sup, 43 | [From, {Cmd, N, Timeout}]), 44 | wait_results(ReqId, Timeout, FoldFun, FoldAcc). 45 | 46 | %% @private 47 | wait_results(ReqId, Timeout, Fun, Acc) -> 48 | receive 49 | {ReqId, {data, Data}} -> 50 | wait_results(ReqId, Timeout, Fun, Fun(Data, Acc)); 51 | {ReqId, done} -> 52 | {ok, Acc}; 53 | {ReqId, {error, Error}} -> 54 | {error, Error} 55 | after Timeout -> 56 | {error, {timeout, Acc}} 57 | end. 58 | 59 | 60 | %%%=================================================================== 61 | %%% Internal 62 | %%%=================================================================== 63 | 64 | -record(state, { 65 | cmd :: term(), 66 | from :: {raw, reference(), pid()} 67 | }). 68 | 69 | 70 | %% @private 71 | %% - Request: Cmd, 72 | %% - NodeSelector: Either the atom `all' to indicate that enough VNodes 73 | %% must be available to achieve a minimal covering set or 'allup' to use 74 | %% whatever VNodes are available even if they do not represent a fully covering set. 75 | %% - NVal: N, 76 | %% - PrimaryVNodeCoverage: The number of primary VNodes from the preference list 77 | %% to use in creating the coverage plan. 78 | %% - NodeCheckService: The service to use to check for available nodes 79 | %% - VNodeMaster: The atom to use to reach the vnode master module. 80 | %% - Timeout - The timeout interval for the coverage request. 81 | %% - State - The initial state for the module 82 | %% 83 | init(From, {Cmd, N, Timeout}) -> 84 | { 85 | Cmd, 86 | all, 87 | N, 88 | 1, 89 | nkdist, 90 | nkdist_vnode_master, 91 | Timeout, 92 | #state{cmd=Cmd, from=From} 93 | }. 94 | 95 | 96 | %% @private 97 | process_results({vnode, _Idx, _Node, {data, Data}}, State) -> 98 | reply({data, Data}, State), 99 | {ok, State}; 100 | 101 | process_results({vnode, _Idx, _Node, {done, Data}}, State) -> 102 | lager:debug("DONE: ~p, ~p, ~p", [nkdist_util:idx2pos(_Idx), _Node, Data]), 103 | reply({data, Data}, State), 104 | {done, State}; 105 | 106 | process_results({vnode, _Idx, _Node, done}, State) -> 107 | {done, State}; 108 | 109 | process_results({vnode, _Idx, _Node, {error, Error}}, State) -> 110 | reply({error, Error}, State), 111 | {done, State}. 112 | 113 | 114 | %% @private 115 | finish(clean, State) -> 116 | reply(done, State), 117 | {stop, normal, State}; 118 | 119 | finish({error, Error}, #state{cmd=Cmd}=State) -> 120 | lager:error("Process results finish error in cmd ~p: ~p", [Cmd, Error]), 121 | reply({error, Error}, State), 122 | {stop, normal, State}. 123 | 124 | 125 | %% =================================================================== 126 | %% Internal functions 127 | %% =================================================================== 128 | 129 | 130 | %% @private 131 | reply(Reply, #state{from={raw, ReqId, Pid}}) -> 132 | Pid ! {ReqId, Reply}. 133 | 134 | 135 | -------------------------------------------------------------------------------- /src/nkdist_gen_server.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% 3 | %% Copyright (c) 2015 Carlos Gonzalez Florido. All Rights Reserved. 4 | %% 5 | %% This file is provided to you under the Apache License, 6 | %% Version 2.0 (the "License"); you may not use this file 7 | %% except in compliance with the License. You may obtain 8 | %% a copy of the License at 9 | %% 10 | %% http://www.apache.org/licenses/LICENSE-2.0 11 | %% 12 | %% Unless required by applicable law or agreed to in writing, 13 | %% software distributed under the License is distributed on an 14 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | %% KIND, either express or implied. See the License for the 16 | %% specific language governing permissions and limitations 17 | %% under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | 21 | %% @doc Master Management 22 | %% This behaviour is nearly equivalent to a standar gen_server that 23 | %% registers locally under the 'Callback' atom, and it is intended to 24 | %% be started at several nodes at the cluster. 25 | %% One of them will be elected master, and the callback handle_master/2 26 | %% will be called with its pid(), or 'undefined' if the vnode fails. 27 | -module(nkdist_gen_server). 28 | -author('Carlos Gonzalez '). 29 | -behaviour(gen_server). 30 | 31 | -export([start_link/3, start/3, get_master/1, get_master/2, call/2, call/3, cast/2]). 32 | -export([init/1, terminate/2, code_change/3, handle_call/3, 33 | handle_cast/2, handle_info/2]). 34 | 35 | 36 | %% =================================================================== 37 | %% Behaviour 38 | %% =================================================================== 39 | 40 | -callback init(Args :: term()) -> 41 | {ok, State :: term()} | {ok, State :: term(), timeout() | hibernate} | 42 | {stop, Reason :: term()} | ignore. 43 | 44 | -callback handle_call(Request :: term(), From :: {pid(), Tag :: term()}, 45 | State :: term()) -> 46 | {reply, Reply :: term(), NewState :: term()} | 47 | {reply, Reply :: term(), NewState :: term(), timeout() | hibernate} | 48 | {noreply, NewState :: term()} | 49 | {noreply, NewState :: term(), timeout() | hibernate} | 50 | {stop, Reason :: term(), Reply :: term(), NewState :: term()} | 51 | {stop, Reason :: term(), NewState :: term()}. 52 | 53 | -callback handle_cast(Request :: term(), State :: term()) -> 54 | {noreply, NewState :: term()} | 55 | {noreply, NewState :: term(), timeout() | hibernate} | 56 | {stop, Reason :: term(), NewState :: term()}. 57 | 58 | -callback handle_info(Info :: timeout | term(), State :: term()) -> 59 | {noreply, NewState :: term()} | 60 | {noreply, NewState :: term(), timeout() | hibernate} | 61 | {stop, Reason :: term(), NewState :: term()}. 62 | 63 | -callback terminate(Reason :: (normal | shutdown | {shutdown, term()} | 64 | term()), 65 | State :: term()) -> 66 | term(). 67 | 68 | -callback code_change(OldVsn :: (term() | {down, term()}), State :: term(), 69 | Extra :: term()) -> 70 | {ok, NewState :: term()} | {error, Reason :: term()}. 71 | 72 | -callback handle_master(pid()|undefined, State :: term()) -> 73 | {ok, NewState :: term()} | {error, Reason :: term(), NewState :: term()}. 74 | 75 | 76 | 77 | %% =================================================================== 78 | %% Public 79 | %% =================================================================== 80 | 81 | 82 | %% @doc Starts the process 83 | -spec start_link(atom(), term(), list()) -> 84 | {ok, pid()} | {error, term}. 85 | 86 | start_link(Callback, Arg, Opts) -> 87 | gen_server:start_link({local, Callback}, ?MODULE, {Callback, Arg}, Opts). 88 | 89 | 90 | %% @doc Starts the process 91 | -spec start(atom(), term(), list()) -> 92 | {ok, pid()} | {error, term}. 93 | 94 | start(Callback, Arg, Opts) -> 95 | gen_server:start({local, Callback}, ?MODULE, {Callback, Arg}, Opts). 96 | 97 | 98 | %% @doc Equivalent to get_master(Callback, 5000). 99 | -spec get_master(atom()) -> 100 | {ok, pid()|undefined}. 101 | 102 | get_master(Callback) -> 103 | get_master(Callback, 5000). 104 | 105 | 106 | %% @doc Gets the current master 107 | -spec get_master(atom(), pos_integer()) -> 108 | {ok, pid()|undefined}. 109 | 110 | get_master(Callback, Timeout) -> 111 | gen_server:call(Callback, nkdist_get_master, Timeout). 112 | 113 | 114 | %% @doc Equivalent to call(Callback, Msg, 5000) 115 | -spec call(atom(), term()) -> 116 | term() | {error, no_master}. 117 | 118 | call(Callback, Msg) -> 119 | call(Callback, Msg, 5000). 120 | 121 | 122 | %% @doc Cals to the current master 123 | -spec call(atom(), term(), pos_integer()|infinity) -> 124 | term() | {error, no_master}. 125 | 126 | call(Callback, Msg, Timeout) -> 127 | case get_master(Callback, Timeout) of 128 | {ok, Pid} when is_pid(Pid) -> 129 | gen_server:call(Pid, Msg, Timeout); 130 | {ok, undefined} -> 131 | {error, no_master} 132 | end. 133 | 134 | 135 | %% @doc Cals to the current master 136 | -spec cast(atom(), term()) -> 137 | ok | {error, no_master}. 138 | 139 | cast(Callback, Msg) -> 140 | case get_master(Callback, 60000) of 141 | {ok, Pid} when is_pid(Pid) -> 142 | gen_server:cast(Pid, Msg); 143 | {ok, undefined} -> 144 | {error, no_master} 145 | end. 146 | 147 | 148 | 149 | % =================================================================== 150 | %% gen_server 151 | %% =================================================================== 152 | 153 | 154 | 155 | -record(state, { 156 | callback :: atom(), 157 | vnode :: pid(), 158 | master :: pid(), 159 | user :: term() 160 | }). 161 | 162 | -define(POSMOD, #state.callback). 163 | -define(POSUSER, #state.user). 164 | 165 | 166 | %% @private 167 | init({Callback, Arg}) -> 168 | State = register(#state{callback=Callback}), 169 | nklib_gen_server:init(Arg, State, ?POSMOD, ?POSUSER). 170 | 171 | 172 | %% @private 173 | handle_call(nkdist_get_master, _From, #state{master=Master}=State) -> 174 | {reply, {ok, Master}, State}; 175 | 176 | handle_call(Msg, From, State) -> 177 | nklib_gen_server:handle_call(Msg, From, State, ?POSMOD, ?POSUSER). 178 | 179 | 180 | %% @private 181 | handle_cast(Msg, State) -> 182 | nklib_gen_server:handle_cast(Msg, State, ?POSMOD, ?POSUSER). 183 | 184 | 185 | %% @private 186 | handle_info(nkdist_register, State) -> 187 | {noreply, register(State)}; 188 | 189 | %% called from the vnode 190 | handle_info({nkdist_master, _Callback, Master}, #state{master=Master}=State) -> 191 | {noreply, State}; 192 | 193 | handle_info({nkdist_master, _Callback, Master}, State) -> 194 | State1 = State#state{master=Master}, 195 | case 196 | nklib_gen_server:handle_any(handle_master, [Master], State1, ?POSMOD, ?POSUSER) 197 | of 198 | {ok, State2} -> {noreply, State2}; 199 | {error, Error, State2} -> {stop, Error, State2} 200 | end; 201 | 202 | handle_info({'DOWN', _Ref, process, Pid, Reason}, #state{vnode=Pid}=State) -> 203 | case Reason of 204 | normal -> lager:info("NkDIST master: vnode has stopped"); 205 | _ -> lager:warning("NkDIST master: vnode has failed: ~p", [Reason]) 206 | end, 207 | self() ! {nkdist_master, State#state.callback, undefined}, 208 | {noreply, register(State)}; 209 | 210 | handle_info(Info, State) -> 211 | nklib_gen_server:handle_info(Info, State, ?POSMOD, ?POSUSER). 212 | 213 | 214 | %% @private 215 | -spec code_change(term(), #state{}, term()) -> 216 | {ok, #state{}}. 217 | 218 | code_change(OldVsn, State, Extra) -> 219 | nklib_gen_server:code_change(OldVsn, State, Extra, ?POSMOD, ?POSUSER). 220 | 221 | 222 | %% @private 223 | -spec terminate(term(), #state{}) -> 224 | ok. 225 | 226 | terminate(Reason, State) -> 227 | nklib_gen_server:terminate(Reason, State, ?POSMOD, ?POSUSER). 228 | 229 | 230 | 231 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Internal %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 232 | 233 | 234 | %% @private 235 | register(#state{callback=Callback}=State) -> 236 | case catch nkdist:register(Callback) of 237 | {ok, VNode} -> 238 | monitor(process, VNode), 239 | State#state{vnode=VNode}; 240 | {error, Error} -> 241 | lager:notice("NkDIST: Callback ~p could not register: ~p", [Callback, Error]), 242 | erlang:send_after(500, self(), nkdist_register), 243 | State; 244 | {'EXIT', Error} -> 245 | lager:notice("NkDIST: Callback ~p could not register: ~p", [Callback, Error]), 246 | erlang:send_after(500, self(), nkdist_register), 247 | State 248 | end. 249 | 250 | 251 | 252 | 253 | 254 | -------------------------------------------------------------------------------- /src/nkdist_proc.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% 3 | %% Copyright (c) 2015 Carlos Gonzalez Florido. All Rights Reserved. 4 | %% 5 | %% This file is provided to you under the Apache License, 6 | %% Version 2.0 (the "License"); you may not use this file 7 | %% except in compliance with the License. You may obtain 8 | %% a copy of the License at 9 | %% 10 | %% http://www.apache.org/licenses/LICENSE-2.0 11 | %% 12 | %% Unless required by applicable law or agreed to in writing, 13 | %% software distributed under the License is distributed on an 14 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | %% KIND, either express or implied. See the License for the 16 | %% specific language governing permissions and limitations 17 | %% under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | 21 | %% @doc Cluster Proc Behaviour 22 | -module(nkdist_proc). 23 | -author('Carlos Gonzalez '). 24 | 25 | 26 | %% =================================================================== 27 | %% Types 28 | %% =================================================================== 29 | 30 | %% @doc Start a new process 31 | %% This callback is called when the start of a new process has been requested 32 | %% using nkdist:start_proc/3. 33 | %% You must start a new Erlang process and return its pid(). 34 | %% If you link to the calling process (the vnode process), you will receive an 35 | %% 'EXIT' when the vnode is shutted down. 36 | -callback start(nkdist:proc_id(), Args::term()) -> 37 | {ok, pid()} | {error, term()}. 38 | 39 | 40 | %% @doc Starts a new clone process 41 | %% This callback is called when an existing process must be moved from an old 42 | %% node to a new node. You receive the pid() of the old (still running) process, 43 | %% so you can: 44 | %% - get any information from it 45 | %% - put it in any "stopping" state so that it does not accept new requests, 46 | %% or return the new pid() 47 | %% - stop the process or schedule its stop once it has no more work to do 48 | -callback start_and_join(nkdist:proc_id(), pid()) -> 49 | {ok, pid()} | {error, term()}. 50 | 51 | 52 | %% @doc Joins two existing processes 53 | %% This callback is called when an existing process must be moved from an old 54 | %% node to a new node, but the process already exists in the new node. 55 | %% This can happen because of a network partition, while the process was 56 | %% stared at both sides of the network 57 | %% You receive the pid() of the 'winning' process, and the pid() of the old 58 | %% process, and must 'join' its states and stop the old one. 59 | -callback join(Current::pid(), Old::pid()) -> 60 | ok | {error, term()}. 61 | 62 | 63 | %% @doc Optional callback to use a custom hashing function 64 | %% -callback get_hash(nkdist:proc_id()) -> 65 | %% binary(). 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /src/nkdist_proc_sample.erl: -------------------------------------------------------------------------------- 1 | -module(nkdist_proc_sample). 2 | -author('Carlos Gonzalez '). 3 | -behaviour(gen_server). 4 | -behaviour(nkdist_proc). 5 | 6 | -export([start/2, start_and_join/2, join/2]). 7 | -export([init/1, terminate/2, code_change/3, handle_call/3, 8 | handle_cast/2, handle_info/2]). 9 | 10 | 11 | %% =================================================================== 12 | %% Proc Behaviour 13 | %% =================================================================== 14 | 15 | 16 | start(ProcId, Args) -> 17 | gen_server:start_link(?MODULE, {proc_id, ProcId, Args}, []). 18 | 19 | start_and_join(ProcId, OldPid) -> 20 | gen_server:start_link(?MODULE, {join, ProcId, OldPid}, []). 21 | 22 | join(Pid, OldPid) -> 23 | gen_server:call(Pid, {join, OldPid}). 24 | 25 | 26 | 27 | %% =================================================================== 28 | %% gen_server 29 | %% =================================================================== 30 | 31 | 32 | -record(state, { 33 | id, 34 | data 35 | }). 36 | 37 | 38 | %% @private 39 | -spec init(term()) -> 40 | {ok, #state{}}. 41 | 42 | init({proc_id, ProcId, _Args}) -> 43 | Data = crypto:rand_uniform(0, 100), 44 | lager:notice("Test server ~p (~p) started with: ~p", [ProcId, self(), Data]), 45 | {ok, #state{id=ProcId, data=Data}}; 46 | 47 | init({join, ProcId, Pid}) -> 48 | case catch gen_server:call(Pid, freeze, infinity) of 49 | {ok, Data} -> 50 | lager:notice("Test server ~p (~p) started from ~p (~p)", 51 | [ProcId, self(), Pid, Data]), 52 | {ok, #state{id=ProcId, data=Data}}; 53 | _ -> 54 | {stop, could_not_start} 55 | end. 56 | 57 | %% @private 58 | -spec handle_call(term(), {pid(), term()}, #state{}) -> 59 | {reply, term(), #state{}} | {noreply, #state{}}. 60 | 61 | handle_call(freeze, _From, #state{data=Data}=State) -> 62 | lager:notice("Test server ~p (~p) freezed", [self(), Data]), 63 | {stop, normal, {ok, Data}, State}; 64 | 65 | handle_call({join, Pid}, _From, #state{id=ProcId, data=Data}=State) -> 66 | case catch gen_server:call(Pid, freeze, infinity) of 67 | {ok, NewData} -> 68 | lager:notice("Test server ~p (~p) joined ~p (~p, ~p)", 69 | [ProcId, self(), Pid, NewData, Data]), 70 | {reply, ok, State#state{data=max(NewData, Data)}}; 71 | _ -> 72 | lager:notice("Test server ~p (~p) could not join ~p", 73 | [ProcId, self(), Pid]), 74 | {reply, {error, could_not_join}, State} 75 | end; 76 | 77 | handle_call(get_data, _From, #state{data=Data}=State) -> 78 | {reply, {ok, Data}, State}; 79 | 80 | handle_call(Msg, _From, State) -> 81 | lager:error("Module ~p received unexpected call: ~p", [?MODULE, Msg]), 82 | {noreply, State}. 83 | 84 | 85 | %% @private 86 | -spec handle_cast(term(), #state{}) -> 87 | {noreply, #state{}}. 88 | 89 | handle_cast(stop, State) -> 90 | {stop, normal, State}; 91 | 92 | handle_cast(Msg, State) -> 93 | lager:error("Module ~p received unexpected cast: ~p", [?MODULE, Msg]), 94 | {noreply, State}. 95 | 96 | 97 | %% @private 98 | -spec handle_info(term(), #state{}) -> 99 | {noreply, #state{}}. 100 | 101 | handle_info(Msg, State) -> 102 | lager:error("Module ~p received unexpected info: ~p", [?MODULE, Msg]), 103 | {noreply, State}. 104 | 105 | 106 | %% @private 107 | -spec code_change(term(), #state{}, term()) -> 108 | {ok, #state{}}. 109 | 110 | code_change(_OldVsn, State, _Extra) -> 111 | {ok, State}. 112 | 113 | 114 | %% @private 115 | -spec terminate(term(), #state{}) -> 116 | ok. 117 | 118 | terminate(_Reason, _State) -> 119 | lager:notice("Test server stopped: ~p", [self()]), 120 | ok. 121 | 122 | 123 | 124 | %% =================================================================== 125 | %% Internal 126 | %% =================================================================== 127 | 128 | 129 | -------------------------------------------------------------------------------- /src/nkdist_sup.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% 3 | %% Copyright (c) 2015 Carlos Gonzalez Florido. All Rights Reserved. 4 | %% 5 | %% This file is provided to you under the Apache License, 6 | %% Version 2.0 (the "License"); you may not use this file 7 | %% except in compliance with the License. You may obtain 8 | %% a copy of the License at 9 | %% 10 | %% http://www.apache.org/licenses/LICENSE-2.0 11 | %% 12 | %% Unless required by applicable law or agreed to in writing, 13 | %% software distributed under the License is distributed on an 14 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | %% KIND, either express or implied. See the License for the 16 | %% specific language governing permissions and limitations 17 | %% under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | 21 | %% @private NkDIST LIB main supervisor 22 | -module(nkdist_sup). 23 | -author('Carlos Gonzalez '). 24 | -behaviour(supervisor). 25 | 26 | -export([init/1, start_link/0]). 27 | -export([start_coverage_fsm_sup/0]). 28 | 29 | -include("nkdist.hrl"). 30 | 31 | %% @private 32 | -spec start_link() -> 33 | {ok, pid()}. 34 | 35 | start_link() -> 36 | ChildsSpec = [ 37 | % {nkdist_vnode_get_fsm_sup, 38 | % {?MODULE, start_vnode_get_fsm_sup, []}, 39 | % permanent, 40 | % infinity, 41 | % supervisor, 42 | % [?MODULE]}, 43 | % {nkdist_vnode_put_fsm_sup, 44 | % {?MODULE, start_vnode_put_fsm_sup, []}, 45 | % permanent, 46 | % infinity, 47 | % supervisor, 48 | % [?MODULE]}, 49 | {nkdist_coverage_sup, 50 | {?MODULE, start_coverage_fsm_sup, []}, 51 | permanent, 52 | infinity, 53 | supervisor, 54 | [?MODULE]}, 55 | {nkdist_vnode_master, 56 | {riak_core_vnode_master, start_link, [nkdist_vnode]}, 57 | permanent, 58 | 5000, 59 | worker, 60 | [riak_core_vnode_master]} 61 | ], 62 | supervisor:start_link({local, ?MODULE}, ?MODULE, {{one_for_one, 10, 60}, 63 | ChildsSpec}). 64 | 65 | 66 | %% @private 67 | init(ChildSpecs) -> 68 | {ok, ChildSpecs}. 69 | 70 | 71 | % %% @private 72 | % start_tasks_sup() -> 73 | % supervisor:start_link({local, nkdist_tasks_sup}, 74 | % ?MODULE, {{one_for_one, 10, 60}, []}). 75 | 76 | 77 | % start_vnode_get_fsm_sup() -> 78 | % supervisor:start_link({local, nkdist_vnode_get_fsm_sup}, ?MODULE, 79 | % {{simple_one_for_one, 3, 60}, [ 80 | % {undefined, 81 | % {nkdist_vnode_get_fsm, start_link, []}, 82 | % temporary, 83 | % 5000, 84 | % worker, 85 | % [nkdist_vnode_get_fsm] 86 | % }]}). 87 | 88 | % start_vnode_put_fsm_sup() -> 89 | % supervisor:start_link({local, nkdist_vnode_put_fsm_sup}, ?MODULE, 90 | % {{simple_one_for_one, 3, 60}, [ 91 | % {undefined, 92 | % {nkdist_vnode_put_fsm, start_link, []}, 93 | % temporary, 94 | % 5000, 95 | % worker, 96 | % [nkdist_vnode_put_fsm] 97 | % }]}). 98 | 99 | start_coverage_fsm_sup() -> 100 | supervisor:start_link({local, nkdist_coverage_sup}, ?MODULE, 101 | {{simple_one_for_one, 3, 60}, [ 102 | {undefined, 103 | {riak_core_coverage_fsm, start_link, [nkdist_coverage]}, 104 | temporary, 105 | 5000, 106 | worker, 107 | [nkdist_coverage] 108 | }]}). 109 | 110 | -------------------------------------------------------------------------------- /src/nkdist_util.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% 3 | %% Copyright (c) 2015 Carlos Gonzalez Florido. All Rights Reserved. 4 | %% 5 | %% This file is provided to you under the Apache License, 6 | %% Version 2.0 (the "License"); you may not use this file 7 | %% except in compliance with the License. You may obtain 8 | %% a copy of the License at 9 | %% 10 | %% http://www.apache.org/licenses/LICENSE-2.0 11 | %% 12 | %% Unless required by applicable law or agreed to in writing, 13 | %% software distributed under the License is distributed on an 14 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | %% KIND, either express or implied. See the License for the 16 | %% specific language governing permissions and limitations 17 | %% under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | 21 | -module(nkdist_util). 22 | -author('Carlos Gonzalez '). 23 | 24 | -export([ensure_dir/0, store_idx_cache/0, idx2pos/0, idx2pos/1, pos2idx/1]). 25 | 26 | 27 | %% =================================================================== 28 | %% Public 29 | %% =================================================================== 30 | 31 | ensure_dir() -> 32 | application:load(riak_core), 33 | {ok, DataDir} = application:get_env(riak_core, platform_data_dir), 34 | RingFile = filename:join([DataDir, "ring", "dummy"]), 35 | filelib:ensure_dir(RingFile). 36 | 37 | 38 | %% @private Stored a mapping from (long) IDX numbers to short indices 39 | store_idx_cache() -> 40 | {ok, Ring} = riak_core_ring_manager:get_my_ring(), 41 | NumParts = riak_core_ring:num_partitions(Ring), 42 | OwnersData = riak_core_ring:all_owners(Ring), 43 | %% 0 to NumParts-1 44 | Idxs2Pos = lists:zip( 45 | lists:seq(0, NumParts-1), 46 | [Idx || {Idx, _N} <- OwnersData] 47 | ), 48 | riak_core_mochiglobal:put(nkdist_idx2pos, Idxs2Pos). 49 | 50 | 51 | %% @doc Converts a (long) Idx to a short pos 52 | idx2pos() -> 53 | 'mochiglobal:nkdist_idx2pos':term(). 54 | 55 | 56 | 57 | idx2pos(Idx) -> 58 | {Pos, Idx} = lists:keyfind(Idx, 2, idx2pos()), 59 | Pos. 60 | 61 | pos2idx(Num) -> 62 | {Num, Idx} = lists:keyfind(Num, 1, idx2pos()), 63 | Idx. 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /src/nkdist_vnode.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% 3 | %% Copyright (c) 2015 Carlos Gonzalez Florido. All Rights Reserved. 4 | %% 5 | %% This file is provided to you under the Apache License, 6 | %% Version 2.0 (the "License"); you may not use this file 7 | %% except in compliance with the License. You may obtain 8 | %% a copy of the License at 9 | %% 10 | %% http://www.apache.org/licenses/LICENSE-2.0 11 | %% 12 | %% Unless required by applicable law or agreed to in writing, 13 | %% software distributed under the License is distributed on an 14 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | %% KIND, either express or implied. See the License for the 16 | %% specific language governing permissions and limitations 17 | %% under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | 21 | %% @private Riak Core VNode behaviour 22 | -module(nkdist_vnode). 23 | -author('Carlos Gonzalez '). 24 | 25 | -behaviour(riak_core_vnode). 26 | 27 | -export([get_info/1, find_proc/3, start_proc/4, register/3]). 28 | 29 | -export([start_vnode/1, 30 | init/1, 31 | terminate/2, 32 | handle_command/3, 33 | is_empty/1, 34 | delete/1, 35 | handle_handoff_command/3, 36 | handoff_starting/2, 37 | handoff_cancelled/1, 38 | handoff_finished/2, 39 | handle_handoff_data/2, 40 | encode_handoff_item/2, 41 | handle_coverage/4, 42 | handle_info/2, 43 | handle_exit/3, 44 | set_vnode_forwarding/2]). 45 | 46 | -include("nkdist.hrl"). 47 | -include_lib("riak_core/include/riak_core_vnode.hrl"). 48 | 49 | 50 | 51 | %%%%%%%%%%%%%%%%%%%%%%%%%%%% External %%%%%%%%%%%%%%%%%%%%%%%%%%%% 52 | 53 | 54 | %% @private 55 | -spec get_info(nkdist:vnode_id()) -> 56 | {ok, map()}. 57 | 58 | get_info({Idx, Node}) -> 59 | spawn_command({Idx, Node}, get_info). 60 | 61 | 62 | %% @private 63 | -spec find_proc(nkdist:vnode_id(), module(), nkdist:proc_id()) -> 64 | {ok, pid()} | {error, not_found}. 65 | 66 | find_proc({Idx, Node}, CallBack, ProcId) -> 67 | spawn_command({Idx, Node}, {find_proc, CallBack, ProcId}). 68 | 69 | 70 | %% @private 71 | -spec start_proc(nkdist:vnode_id(), module(), nkdist:proc_id(), term()) -> 72 | {ok, pid()} | {error, {already_started, pid()}} | {error, term()}. 73 | 74 | start_proc({Idx, Node}, CallBack, ProcId, Args) -> 75 | spawn_command({Idx, Node}, {start_proc, CallBack, ProcId, Args}). 76 | 77 | 78 | %% @private 79 | -spec register(nkdist:vnode_id(), atom(), pid()) -> 80 | {ok, VNode::pid()} | {error, term()}. 81 | 82 | 83 | register({Idx, Node}, Name, Pid) -> 84 | command({Idx, Node}, {register, Name, Pid}). 85 | 86 | 87 | %% @private 88 | %% Sends a synchronous request to the vnode. 89 | %% If it fails, it will launch an exception 90 | -spec spawn_command(nkdist:vnode_id(), term()) -> 91 | {ok, term()} | {error, term()}. 92 | 93 | spawn_command({Idx, Node}, Msg) -> 94 | riak_core_vnode_master:sync_spawn_command({Idx, Node}, Msg, ?VMASTER). 95 | 96 | 97 | %% @private 98 | %% Sends a synchronous request to the vnode. 99 | %% If it fails, it will launch an exception 100 | -spec command(nkdist:vnode_id(), term()) -> 101 | {ok, term()} | {error, term()}. 102 | 103 | command({Idx, Node}, Msg) -> 104 | riak_core_vnode_master:sync_command({Idx, Node}, Msg, ?VMASTER). 105 | 106 | 107 | %% @private 108 | start_vnode(I) -> 109 | riak_core_vnode_master:get_vnode_pid(I, ?MODULE). 110 | 111 | 112 | 113 | 114 | 115 | %%%%%%%%%%%%%%%%%%%%%%%%%%%% VNode Behaviour %%%%%%%%%%%%%%%%%%%%%%%%%%%% 116 | 117 | -record(state, { 118 | idx :: chash:index_as_int(), % vnode's index 119 | pos :: integer(), 120 | procs :: #{{module(), nkdist:proc_id()} => pid()}, 121 | proc_pids :: #{pid() => {module(), nkdist:proc_id()}}, 122 | masters :: #{atom() => [pid()]}, % first pid is master 123 | master_pids :: #{pid() => atom()}, 124 | handoff_target :: {chash:index_as_int(), node()}, 125 | forward :: node() | [{integer(), node()}] 126 | }). 127 | 128 | 129 | %% @private 130 | init([Idx]) -> 131 | State = #state{ 132 | idx = Idx, 133 | pos = nkdist_util:idx2pos(Idx), 134 | procs = #{}, 135 | proc_pids = #{}, 136 | masters = #{}, 137 | master_pids = #{} 138 | }, 139 | Workers = application:get_env(?APP, vnode_workers, 1), 140 | FoldWorkerPool = {pool, nkdist_vnode_worker, Workers, []}, 141 | {ok, State, [FoldWorkerPool]}. 142 | 143 | 144 | %% @private 145 | handle_command(get_info, _Sender, State) -> 146 | {reply, {ok, do_get_info(State)}, State}; 147 | 148 | 149 | handle_command({find_proc, CallBack, ProcId}, _Sender, State) -> 150 | case do_find_proc(CallBack, ProcId, State) of 151 | {ok, Pid} -> 152 | {reply, {ok, Pid}, State}; 153 | not_found -> 154 | {reply, {error, not_found}, State} 155 | end; 156 | 157 | handle_command({start_proc, CallBack, ProcId, Args}, _Sender, State) -> 158 | case do_find_proc(CallBack, ProcId, State) of 159 | {ok, Pid} -> 160 | {reply, {error, {already_started, Pid}}, State}; 161 | not_found -> 162 | try 163 | case CallBack:start(ProcId, Args) of 164 | {ok, Pid} -> 165 | State1 = started_proc(CallBack, ProcId, Pid, State), 166 | {reply, {ok, Pid}, State1}; 167 | {error, Error} -> 168 | {reply, {error, Error}, State} 169 | end 170 | catch 171 | C:E-> 172 | {reply, {error, {{C, E}, erlang:get_stacktrace()}}, State} 173 | end 174 | end; 175 | 176 | handle_command({register, Name, Pid}, _Send, State) -> 177 | State1 = do_register(Name, Pid, State), 178 | {reply, {ok, self()}, State1}; 179 | 180 | handle_command(Message, _Sender, State) -> 181 | lager:warning("NkDIST vnode: Unhandled command: ~p, ~p", [Message, _Sender]), 182 | {reply, {error, unhandled_command}, State}. 183 | 184 | 185 | %% @private 186 | handle_coverage(get_procs, _KeySpaces, _Sender, State) -> 187 | #state{procs=Procs, idx=Idx} = State, 188 | Data = maps:to_list(Procs), 189 | {reply, {vnode, Idx, node(), {done, Data}}, State}; 190 | 191 | handle_coverage({get_procs, CallBack}, _KeySpaces, _Sender, State) -> 192 | #state{procs=Procs, idx=Idx} = State, 193 | Data = [{ProcId, Pid} || {{C, ProcId}, Pid} <- maps:to_list(Procs), C==CallBack], 194 | {reply, {vnode, Idx, node(), {done, Data}}, State}; 195 | 196 | handle_coverage(get_masters, _KeySpaces, _Sender, State) -> 197 | #state{masters=Masters, idx=Idx} = State, 198 | {reply, {vnode, Idx, node(), {done, Masters}}, State}; 199 | 200 | handle_coverage(get_info, _KeySpaces, _Sender, #state{idx=Idx}=State) -> 201 | {reply, {vnode, Idx, node(), {done, do_get_info(State)}}, State}; 202 | 203 | handle_coverage(Cmd, _KeySpaces, _Sender, State) -> 204 | lager:error("Module ~p unknown coverage: ~p", [?MODULE, Cmd]), 205 | {noreply, State}. 206 | 207 | 208 | %% @private 209 | handle_handoff_command(?FOLD_REQ{foldfun=Fun, acc0=Acc0}, Sender, State) -> 210 | #state{masters=Masters, procs=Procs} = State, 211 | MastersData = maps:to_list(Masters), 212 | ProcsData = maps:to_list(Procs), 213 | {async, {handoff, MastersData, ProcsData, Fun, Acc0}, Sender, State}; 214 | 215 | handle_handoff_command({find_proc, CallBack, ProcId}, _Sender, State) -> 216 | case do_find_proc(CallBack, ProcId, State) of 217 | {ok, Pid} -> 218 | {reply, {ok, Pid}, State}; 219 | not_found -> 220 | {forwward, State} 221 | end; 222 | 223 | handle_handoff_command(Term, _Sender, State) when 224 | element(1, Term)==register; element(1, Term)==start_proc -> 225 | {forward, State}; 226 | 227 | % Process rest of operations locally 228 | handle_handoff_command(Cmd, Sender, State) -> 229 | lager:info("NkDIST handoff command ~p at ~p", [Cmd, State#state.pos]), 230 | handle_command(Cmd, Sender, State). 231 | 232 | 233 | %% @private 234 | handoff_starting({Type, {Idx, Node}}, #state{pos=Pos}=State) -> 235 | lager:info("NkDIST handoff (~p) starting at ~p to ~p", [Type, Pos, Node]), 236 | {true, State#state{handoff_target={Idx, Node}}}. 237 | 238 | 239 | %% @private 240 | handoff_cancelled(#state{masters=Masters, pos=Pos}=State) -> 241 | lager:notice("NkDIST handoff cancelled at ~p", [Pos]), 242 | lists:foreach( 243 | fun({Name, Pids}) -> send_master(Name, Pids) end, 244 | maps:to_list(Masters)), 245 | {ok, State#state{handoff_target=undefined}}. 246 | 247 | 248 | %% @private 249 | handoff_finished({_Idx, Node}, #state{pos=Pos}=State) -> 250 | lager:info("NkDIST handoff finished at ~p to ~p", [Pos, Node]), 251 | {ok, State#state{handoff_target=undefined}}. 252 | 253 | 254 | %% @private 255 | %% If we reply {error, ...}, the handoff is cancelled, and riak_core will retry it 256 | %% again and again 257 | handle_handoff_data(BinObj, State) -> 258 | try 259 | case binary_to_term(zlib:unzip(BinObj)) of 260 | {{proc, CallBack, ProcId}, OldPid} -> 261 | case do_find_proc(CallBack, ProcId, State) of 262 | not_found -> 263 | case CallBack:start_and_join(ProcId, OldPid) of 264 | {ok, NewPid} -> 265 | State1 = started_proc(CallBack, ProcId, NewPid, State), 266 | {reply, ok, State1}; 267 | {error, Error} -> 268 | {reply, {error, Error}, State} 269 | end; 270 | {ok, NewPid} -> 271 | case CallBack:join(NewPid, OldPid) of 272 | ok -> 273 | {reply, ok, State}; 274 | {error, Error} -> 275 | {reply, {error, Error}, State} 276 | end 277 | end; 278 | {{master, Name}, Pids} -> 279 | State1 = lists:foldl( 280 | fun(Pid, Acc) -> do_register(Name, Pid, Acc) end, 281 | State, 282 | Pids), 283 | {reply, ok, State1} 284 | end 285 | catch 286 | C:E -> 287 | {reply, {error, {{C, E}, erlang:get_stacktrace()}}, State} 288 | end. 289 | 290 | 291 | %% @private 292 | encode_handoff_item(Key, Val) -> 293 | zlib:zip(term_to_binary({Key, Val})). 294 | 295 | 296 | %% @private 297 | is_empty(#state{procs=Procs, masters=Masters}=State) -> 298 | {maps:size(Procs)+maps:size(Masters)==0, State}. 299 | 300 | 301 | %% @private 302 | delete(#state{pos=Pos}=State) -> 303 | lager:info("NkDIST vnode ~p deleting", [Pos]), 304 | {ok, State}. 305 | 306 | 307 | %% @private 308 | handle_info({'DOWN', _Ref, process, Pid, Reason}, #state{pos=Pos}=State) -> 309 | case check_down_proc(Pid, Reason, State) of 310 | #state{} = State1 -> 311 | {ok, State1}; 312 | undefined -> 313 | case check_down_master(Pid, Reason, State) of 314 | #state{} = State1 -> 315 | {ok, State1}; 316 | undefined -> 317 | lager:info("NkDIST vnode ~p unexpected down (~p, ~p)", 318 | [Pos, Pid, Reason]), 319 | {ok, State} 320 | end 321 | end; 322 | 323 | handle_info(Msg, State) -> 324 | lager:warning("Module ~p unexpected info: ~p", [?MODULE, Msg]), 325 | {ok, State}. 326 | 327 | 328 | %% @private 329 | %% Procs tipically will link to us 330 | handle_exit(Pid, Reason, #state{pos=Pos}=State) -> 331 | case Reason of 332 | normal -> ok; 333 | _ -> lager:debug("NkDIST vnode ~p unhandled EXIT ~p, ~p", [Pos, Pid, Reason]) 334 | end, 335 | {noreply, State}. 336 | 337 | 338 | %% @private 339 | terminate(normal, _State) -> 340 | ok; 341 | 342 | terminate(Reason, #state{pos=Pos}) -> 343 | lager:debug("NkDIST vnode ~p terminate (~p)", [Pos, Reason]). 344 | 345 | 346 | %% @private Called from riak core on forwarding state, after the handoff has been 347 | %% completed, but before the new vnode is marked as the owner of the partition 348 | set_vnode_forwarding(Forward, State) -> 349 | State#state{forward=Forward}. 350 | 351 | 352 | 353 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 354 | 355 | 356 | %% @private 357 | do_get_info(#state{idx=Idx, pos=Pos, procs=Procs, masters=Masters}=S) -> 358 | Data = #{ 359 | pid => self(), 360 | idx => Idx, 361 | procs => Procs, 362 | procs_pids => S#state.proc_pids, 363 | masters => Masters, 364 | master_pids => S#state.master_pids 365 | }, 366 | {Pos, Data}. 367 | 368 | 369 | 370 | %% @private 371 | do_find_proc(CallBack, ProcId, #state{procs=Procs}) -> 372 | case maps:get({CallBack, ProcId}, Procs, undefined) of 373 | Pid when is_pid(Pid) -> 374 | {ok, Pid}; 375 | undefined -> 376 | not_found 377 | end. 378 | 379 | 380 | do_register(Name, Pid, #state{masters=Masters, master_pids=Pids}=State) -> 381 | MasterPids = maps:get(Name, Masters, []), 382 | case lists:member(Pid, MasterPids) of 383 | true -> 384 | send_master(Name, MasterPids), 385 | State; 386 | false -> 387 | monitor(process, Pid), 388 | MasterPids1 = MasterPids ++ [Pid], 389 | send_master(Name, MasterPids1), 390 | Masters1 = maps:put(Name, MasterPids1, Masters), 391 | Pids1 = maps:put(Pid, Name, Pids), 392 | State#state{masters=Masters1, master_pids=Pids1} 393 | end. 394 | 395 | 396 | %% @private 397 | started_proc(CallBack, ProcId, Pid, #state{procs=Procs, proc_pids=Pids}=State) -> 398 | monitor(process, Pid), 399 | Procs1 = maps:put({CallBack, ProcId}, Pid, Procs), 400 | Pids1 = maps:put(Pid, {CallBack, ProcId}, Pids), 401 | State#state{procs=Procs1, proc_pids=Pids1}. 402 | 403 | 404 | %% @private Elects master as first pid on this node 405 | send_master(Name, [Master|_]=Pids) -> 406 | lists:foreach(fun(Pid) -> Pid ! {nkdist_master, Name, Master} end, Pids). 407 | 408 | 409 | %% @private 410 | check_down_proc(Pid, Reason, #state{procs=Procs, proc_pids=Pids}=State) -> 411 | case maps:get(Pid, Pids, undefined) of 412 | undefined -> 413 | undefined; 414 | {CallBack, ProcId} -> 415 | case Reason of 416 | normal -> 417 | ok; 418 | _ -> 419 | lager:info("NkDIST proc '~p' ~p down (~p)", 420 | [CallBack, ProcId, Reason]) 421 | end, 422 | Procs1 = maps:remove({CallBack, ProcId}, Procs), 423 | Pids1 = maps:remove(Pid, Pids), 424 | State#state{procs=Procs1, proc_pids=Pids1} 425 | end. 426 | 427 | %% @private 428 | check_down_master(Pid, Reason, #state{masters=Masters, master_pids=Pids}=State) -> 429 | case maps:get(Pid, Pids, undefined) of 430 | undefined -> 431 | undefined; 432 | Name -> 433 | lager:info("NkDIST master '~p' down (~p, ~p)", [Name, Pid, Reason]), 434 | MasterPids = maps:get(Name, Masters), 435 | case MasterPids -- [Pid] of 436 | [] -> 437 | Masters1 = maps:remove(Name, Masters), 438 | Pids1 = maps:remove(Pid, Pids), 439 | State#state{masters=Masters1, master_pids=Pids1}; 440 | MasterPids1 -> 441 | case State#state.handoff_target of 442 | undefined -> send_master(Name, MasterPids1); 443 | _ -> ok 444 | end, 445 | Masters1 = maps:put(Name, MasterPids1, Masters), 446 | Pids1 = maps:remove(Pid, Pids), 447 | State#state{masters=Masters1, master_pids=Pids1} 448 | end 449 | end. 450 | 451 | 452 | -------------------------------------------------------------------------------- /src/nkdist_vnode_worker.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% 3 | %% Copyright (c) 2015 Carlos Gonzalez Florido. All Rights Reserved. 4 | %% 5 | %% This file is provided to you under the Apache License, 6 | %% Version 2.0 (the "License"); you may not use this file 7 | %% except in compliance with the License. You may obtain 8 | %% a copy of the License at 9 | %% 10 | %% http://www.apache.org/licenses/LICENSE-2.0 11 | %% 12 | %% Unless required by applicable law or agreed to in writing, 13 | %% software distributed under the License is distributed on an 14 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | %% KIND, either express or implied. See the License for the 16 | %% specific language governing permissions and limitations 17 | %% under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | 21 | %% @private Worker module for vnode 22 | -module(nkdist_vnode_worker). 23 | -author('Carlos Gonzalez '). 24 | 25 | -behaviour(riak_core_vnode_worker). 26 | 27 | -export([init_worker/3, handle_work/3]). 28 | 29 | -include("nkdist.hrl"). 30 | 31 | -record(state, { 32 | idx :: chash:index_as_int() 33 | }). 34 | 35 | %% =================================================================== 36 | %% Public API 37 | %% =================================================================== 38 | 39 | %% @private 40 | init_worker(VNodeIndex, [], _Props) -> 41 | {ok, #state{idx=VNodeIndex}}. 42 | 43 | 44 | %% @private 45 | handle_work({handoff, Masters, Procs, Fun, Acc}, Sender, State) -> 46 | Acc1 = do_master_handoff(Masters, Fun, Acc), 47 | Acc2 = do_proc_handoff(Procs, Fun, Acc1), 48 | riak_core_vnode:reply(Sender, Acc2), 49 | {noreply, State}. 50 | 51 | 52 | 53 | %%%=================================================================== 54 | %%% Internal 55 | %%%=================================================================== 56 | 57 | 58 | 59 | %% @private 60 | do_master_handoff([], _Fun, Acc) -> 61 | Acc; 62 | 63 | do_master_handoff([{Name, Pids}|Rest], Fun, Acc) -> 64 | Acc1 = Fun({master, Name}, Pids, Acc), 65 | do_master_handoff(Rest, Fun, Acc1). 66 | 67 | 68 | %% @private 69 | do_proc_handoff([], _Fun, Acc) -> 70 | Acc; 71 | 72 | do_proc_handoff([{{CallBack, ProcId}, Pid}|Rest], Fun, Acc) -> 73 | Acc1 = Fun({proc, CallBack, ProcId}, Pid, Acc), 74 | do_proc_handoff(Rest, Fun, Acc1). -------------------------------------------------------------------------------- /util/dev1.config: -------------------------------------------------------------------------------- 1 | [ 2 | {nkdist, [ 3 | ]}, 4 | 5 | {lager, [ 6 | {handlers, [ 7 | {lager_console_backend, info}, 8 | {lager_file_backend, [{file, "dev/1/log/error.log"}, {level, error}]}, 9 | {lager_file_backend, [{file, "dev/1/log/console.log"}, {level, info}]} 10 | ]}, 11 | {error_logger_redirect, false}, 12 | {crash_log, "log/crash.log"}, 13 | {colored, true}, 14 | {colors, [ 15 | {debug, "\e[0;38m" }, 16 | {info, "\e[0;32m" }, 17 | {notice, "\e[1;36m" }, 18 | {warning, "\e[1;33m" }, 19 | {error, "\e[1;31m" } 20 | ]} 21 | ]}, 22 | 23 | {sasl, [ 24 | {sasl_error_logger, false} 25 | ]}, 26 | 27 | {riak_core, [ 28 | {schema_dirs, ["util"]}, 29 | {enable_consensus, false}, 30 | 31 | 32 | %% Cluster name 33 | {cluster_name, "default"}, 34 | 35 | %% Default location for ring, cluster and other data files 36 | {platform_data_dir, "dev/1/data"}, 37 | 38 | %% Default ring creation size. Make sure it is a power of 2, 39 | %% e.g. 16, 32, 64, 128, 256, 512 etc 40 | {ring_creation_size, 8}, 41 | 42 | %% Default gossip interval (milliseconds) 43 | {gossip_interval, 60000}, 44 | 45 | %% Target N value 46 | {target_n_val, 4}, 47 | 48 | %% Default claims functions 49 | {wants_claim_fun, {riak_core_claim, default_wants_claim}}, 50 | {choose_claim_fun, {riak_core_claim, default_choose_claim}}, 51 | 52 | %% Vnode inactivity timeout (how often to check if fallback vnodes 53 | %% should return their data) in ms. 54 | {vnode_inactivity_timeout, 10000}, 55 | 56 | %% Number of VNodes allowed to do handoff concurrently. 57 | {handoff_concurrency, 2}, 58 | 59 | %% Disable Nagle on HTTP sockets 60 | {disable_http_nagle, true}, 61 | 62 | %% Handoff IP/port 63 | {handoff_port, 8101}, 64 | {handoff_ip, "0.0.0.0"}, 65 | 66 | %% Disterl buffer sizes in bytes. 67 | %% These sizes (3*128*1024 & 6*128*1024) were 68 | %% derived from a limited amount of testing in a 69 | %% 10GE environment, and may need tuning for your 70 | %% network and workload. In particular they're likely 71 | %% too small to be optimal for larger object sizes. 72 | {dist_send_buf_size, 393216}, 73 | {dist_recv_buf_size, 786432} 74 | ]} 75 | ]. 76 | -------------------------------------------------------------------------------- /util/dev2.config: -------------------------------------------------------------------------------- 1 | [ 2 | {nkdist, [ 3 | ]}, 4 | 5 | {lager, [ 6 | {handlers, [ 7 | {lager_console_backend, info}, 8 | {lager_file_backend, [{file, "dev/2/log/error.log"}, {level, error}]}, 9 | {lager_file_backend, [{file, "dev/2/log/console.log"}, {level, info}]} 10 | ]}, 11 | {error_logger_redirect, false}, 12 | {crash_log, "log/crash.log"}, 13 | {colored, true}, 14 | {colors, [ 15 | {debug, "\e[0;38m" }, 16 | {info, "\e[0;32m" }, 17 | {notice, "\e[1;36m" }, 18 | {warning, "\e[1;33m" }, 19 | {error, "\e[1;31m" } 20 | ]} 21 | ]}, 22 | 23 | {sasl, [ 24 | {sasl_error_logger, false} 25 | ]}, 26 | 27 | {riak_core, [ 28 | {schema_dirs, ["util"]}, 29 | {enable_consensus, false}, 30 | 31 | 32 | %% Cluster name 33 | {cluster_name, "default"}, 34 | 35 | %% Default location for ring, cluster and other data files 36 | {platform_data_dir, "dev/2/data"}, 37 | 38 | %% Default ring creation size. Make sure it is a power of 2, 39 | %% e.g. 16, 32, 64, 128, 256, 512 etc 40 | {ring_creation_size, 8}, 41 | 42 | %% Default gossip interval (milliseconds) 43 | {gossip_interval, 60000}, 44 | 45 | %% Target N value 46 | {target_n_val, 4}, 47 | 48 | %% Default claims functions 49 | {wants_claim_fun, {riak_core_claim, default_wants_claim}}, 50 | {choose_claim_fun, {riak_core_claim, default_choose_claim}}, 51 | 52 | %% Vnode inactivity timeout (how often to check if fallback vnodes 53 | %% should return their data) in ms. 54 | {vnode_inactivity_timeout, 10000}, 55 | 56 | %% Number of VNodes allowed to do handoff concurrently. 57 | {handoff_concurrency, 2}, 58 | 59 | %% Disable Nagle on HTTP sockets 60 | {disable_http_nagle, true}, 61 | 62 | %% Handoff IP/port 63 | {handoff_port, 8102}, 64 | {handoff_ip, "0.0.0.0"}, 65 | 66 | %% Disterl buffer sizes in bytes. 67 | %% These sizes (3*128*1024 & 6*128*1024) were 68 | %% derived from a limited amount of testing in a 69 | %% 10GE environment, and may need tuning for your 70 | %% network and workload. In particular they're likely 71 | %% too small to be optimal for larger object sizes. 72 | {dist_send_buf_size, 393216}, 73 | {dist_recv_buf_size, 786432} 74 | ]} 75 | ]. 76 | -------------------------------------------------------------------------------- /util/dev3.config: -------------------------------------------------------------------------------- 1 | [ 2 | {nkdist, [ 3 | ]}, 4 | 5 | {lager, [ 6 | {handlers, [ 7 | {lager_console_backend, info}, 8 | {lager_file_backend, [{file, "dev/3/log/error.log"}, {level, error}]}, 9 | {lager_file_backend, [{file, "dev/3/log/console.log"}, {level, info}]} 10 | ]}, 11 | {error_logger_redirect, false}, 12 | {crash_log, "log/crash.log"}, 13 | {colored, true}, 14 | {colors, [ 15 | {debug, "\e[0;38m" }, 16 | {info, "\e[0;32m" }, 17 | {notice, "\e[1;36m" }, 18 | {warning, "\e[1;33m" }, 19 | {error, "\e[1;31m" } 20 | ]} 21 | ]}, 22 | 23 | {sasl, [ 24 | {sasl_error_logger, false} 25 | ]}, 26 | 27 | {riak_core, [ 28 | {schema_dirs, ["util"]}, 29 | {enable_consensus, false}, 30 | 31 | 32 | %% Cluster name 33 | {cluster_name, "default"}, 34 | 35 | %% Default location for ring, cluster and other data files 36 | {platform_data_dir, "dev/3/data"}, 37 | 38 | %% Default ring creation size. Make sure it is a power of 2, 39 | %% e.g. 16, 32, 64, 128, 256, 512 etc 40 | {ring_creation_size, 8}, 41 | 42 | %% Default gossip interval (milliseconds) 43 | {gossip_interval, 60000}, 44 | 45 | %% Target N value 46 | {target_n_val, 4}, 47 | 48 | %% Default claims functions 49 | {wants_claim_fun, {riak_core_claim, default_wants_claim}}, 50 | {choose_claim_fun, {riak_core_claim, default_choose_claim}}, 51 | 52 | %% Vnode inactivity timeout (how often to check if fallback vnodes 53 | %% should return their data) in ms. 54 | {vnode_inactivity_timeout, 10000}, 55 | 56 | %% Number of VNodes allowed to do handoff concurrently. 57 | {handoff_concurrency, 2}, 58 | 59 | %% Disable Nagle on HTTP sockets 60 | {disable_http_nagle, true}, 61 | 62 | %% Handoff IP/port 63 | {handoff_port, 8103}, 64 | {handoff_ip, "0.0.0.0"}, 65 | 66 | %% Disterl buffer sizes in bytes. 67 | %% These sizes (3*128*1024 & 6*128*1024) were 68 | %% derived from a limited amount of testing in a 69 | %% 10GE environment, and may need tuning for your 70 | %% network and workload. In particular they're likely 71 | %% too small to be optimal for larger object sizes. 72 | {dist_send_buf_size, 393216}, 73 | {dist_recv_buf_size, 786432} 74 | ]} 75 | ]. 76 | -------------------------------------------------------------------------------- /util/dev4.config: -------------------------------------------------------------------------------- 1 | [ 2 | {nkdist, [ 3 | ]}, 4 | 5 | {lager, [ 6 | {handlers, [ 7 | {lager_console_backend, info}, 8 | {lager_file_backend, [{file, "dev/4/log/error.log"}, {level, error}]}, 9 | {lager_file_backend, [{file, "dev/4/log/console.log"}, {level, info}]} 10 | ]}, 11 | {error_logger_redirect, false}, 12 | {crash_log, "log/crash.log"}, 13 | {colored, true}, 14 | {colors, [ 15 | {debug, "\e[0;38m" }, 16 | {info, "\e[0;32m" }, 17 | {notice, "\e[1;36m" }, 18 | {warning, "\e[1;33m" }, 19 | {error, "\e[1;31m" } 20 | ]} 21 | ]}, 22 | 23 | {sasl, [ 24 | {sasl_error_logger, false} 25 | ]}, 26 | 27 | {riak_core, [ 28 | {schema_dirs, ["util"]}, 29 | {enable_consensus, false}, 30 | 31 | 32 | %% Cluster name 33 | {cluster_name, "default"}, 34 | 35 | %% Default location for ring, cluster and other data files 36 | {platform_data_dir, "dev/4/data"}, 37 | 38 | %% Default ring creation size. Make sure it is a power of 2, 39 | %% e.g. 16, 32, 64, 128, 256, 512 etc 40 | {ring_creation_size, 8}, 41 | 42 | %% Default gossip interval (milliseconds) 43 | {gossip_interval, 60000}, 44 | 45 | %% Target N value 46 | {target_n_val, 4}, 47 | 48 | %% Default claims functions 49 | {wants_claim_fun, {riak_core_claim, default_wants_claim}}, 50 | {choose_claim_fun, {riak_core_claim, default_choose_claim}}, 51 | 52 | %% Vnode inactivity timeout (how often to check if fallback vnodes 53 | %% should return their data) in ms. 54 | {vnode_inactivity_timeout, 60000}, 55 | 56 | %% Number of VNodes allowed to do handoff concurrently. 57 | {handoff_concurrency, 2}, 58 | 59 | %% Disable Nagle on HTTP sockets 60 | {disable_http_nagle, true}, 61 | 62 | %% Handoff IP/port 63 | {handoff_port, 8104}, 64 | {handoff_ip, "0.0.0.0"}, 65 | 66 | %% Disterl buffer sizes in bytes. 67 | %% These sizes (3*128*1024 & 6*128*1024) were 68 | %% derived from a limited amount of testing in a 69 | %% 10GE environment, and may need tuning for your 70 | %% network and workload. In particular they're likely 71 | %% too small to be optimal for larger object sizes. 72 | {dist_send_buf_size, 393216}, 73 | {dist_recv_buf_size, 786432} 74 | ]} 75 | ]. 76 | -------------------------------------------------------------------------------- /util/dev5.config: -------------------------------------------------------------------------------- 1 | [ 2 | {nkdist, [ 3 | ]}, 4 | 5 | {lager, [ 6 | {handlers, [ 7 | {lager_console_backend, info}, 8 | {lager_file_backend, [{file, "dev/5/log/error.log"}, {level, error}]}, 9 | {lager_file_backend, [{file, "dev/5/log/console.log"}, {level, info}]} 10 | ]}, 11 | {error_logger_redirect, false}, 12 | {crash_log, "log/crash.log"}, 13 | {colored, true}, 14 | {colors, [ 15 | {debug, "\e[0;38m" }, 16 | {info, "\e[0;32m" }, 17 | {notice, "\e[1;36m" }, 18 | {warning, "\e[1;33m" }, 19 | {error, "\e[1;31m" } 20 | ]} 21 | ]}, 22 | 23 | {sasl, [ 24 | {sasl_error_logger, false} 25 | ]}, 26 | 27 | {riak_core, [ 28 | {schema_dirs, ["util"]}, 29 | {enable_consensus, false}, 30 | 31 | 32 | %% Cluster name 33 | {cluster_name, "default"}, 34 | 35 | %% Default location for ring, cluster and other data files 36 | {platform_data_dir, "dev/5/data"}, 37 | 38 | %% Default ring creation size. Make sure it is a power of 2, 39 | %% e.g. 16, 32, 64, 128, 256, 512 etc 40 | {ring_creation_size, 8}, 41 | 42 | %% Default gossip interval (milliseconds) 43 | {gossip_interval, 60000}, 44 | 45 | %% Target N value 46 | {target_n_val, 4}, 47 | 48 | %% Default claims functions 49 | {wants_claim_fun, {riak_core_claim, default_wants_claim}}, 50 | {choose_claim_fun, {riak_core_claim, default_choose_claim}}, 51 | 52 | %% Vnode inactivity timeout (how often to check if fallback vnodes 53 | %% should return their data) in ms. 54 | {vnode_inactivity_timeout, 60000}, 55 | 56 | %% Number of VNodes allowed to do handoff concurrently. 57 | {handoff_concurrency, 2}, 58 | 59 | %% Disable Nagle on HTTP sockets 60 | {disable_http_nagle, true}, 61 | 62 | %% Handoff IP/port 63 | {handoff_port, 8105}, 64 | {handoff_ip, "0.0.0.0"}, 65 | 66 | %% Disterl buffer sizes in bytes. 67 | %% These sizes (3*128*1024 & 6*128*1024) were 68 | %% derived from a limited amount of testing in a 69 | %% 10GE environment, and may need tuning for your 70 | %% network and workload. In particular they're likely 71 | %% too small to be optimal for larger object sizes. 72 | {dist_send_buf_size, 393216}, 73 | {dist_recv_buf_size, 786432} 74 | ]} 75 | ]. 76 | -------------------------------------------------------------------------------- /util/dev_vm.args: -------------------------------------------------------------------------------- 1 | -pa deps/basho_stats/ebin 2 | -pa deps/bear/ebin 3 | -pa deps/clique/ebin 4 | -pa deps/cluster_info/ebin 5 | -pa deps/cuttlefish/ebin 6 | -pa deps/edown/ebin 7 | -pa deps/eleveldb/ebin 8 | -pa deps/eper/ebin 9 | -pa deps/exometer_core/ebin 10 | -pa deps/folsom/ebin 11 | -pa deps/goldrush/ebin 12 | -pa deps/jsx/ebin 13 | -pa deps/lager/ebin 14 | -pa deps/meck/ebin 15 | -pa deps/neotoma/ebin 16 | -pa deps/nklib/ebin 17 | -pa deps/parse_trans/ebin 18 | -pa deps/pbkdf2/ebin 19 | -pa deps/poolboy/ebin 20 | -pa deps/riak_core/ebin 21 | -pa deps/riak_dt/ebin 22 | -pa deps/riak_ensemble/ebin 23 | -pa deps/riak_sysmon/ebin 24 | -pa deps/setup/ebin 25 | -pa deps/sext/ebin 26 | -pa ../nkdist/ebin 27 | 28 | ## Name of the node 29 | #-name nkcluster@127.0.0.1 30 | -setcookie nk 31 | 32 | ## More processes 33 | +P 1000000 34 | 35 | ## Treat error_logger warnings as warnings 36 | +W w 37 | 38 | ## Increase number of concurrent ports/sockets 39 | -env ERL_MAX_PORTS 65535 40 | 41 | ## Tweak GC to run more often 42 | #-env ERL_FULLSWEEP_AFTER 0 43 | 44 | ## Set the location of crash dumps 45 | -env ERL_CRASH_DUMP . 46 | -------------------------------------------------------------------------------- /util/riak_core.schema: -------------------------------------------------------------------------------- 1 | %%-*- mode: erlang -*- 2 | %% Default Bucket Properties 3 | 4 | %% @doc The number of replicas stored. Note: See Replication 5 | %% Properties for further discussion. 6 | %% http://docs.basho.com/riak/latest/dev/advanced/cap-controls/ 7 | {mapping, "buckets.default.n_val", "riak_core.default_bucket_props.n_val", [ 8 | {datatype, integer}, 9 | {default, 3}, 10 | hidden 11 | ]}. 12 | 13 | %% @doc Number of partitions in the cluster (only valid when first 14 | %% creating the cluster). Must be a power of 2, minimum 8 and maximum 15 | %% 1024. 16 | {mapping, "ring_size", "riak_core.ring_creation_size", [ 17 | {datatype, integer}, 18 | {default, 64}, 19 | {validators, ["ring_size^2", "ring_size_max", "ring_size_min"]}, 20 | {commented, 64} 21 | ]}. 22 | 23 | %% ring_size validators 24 | {validator, "ring_size_max", "2048 and larger are supported, but considered advanced config", 25 | fun(Size) -> 26 | Size =< 1024 27 | end}. 28 | 29 | {validator, "ring_size^2", "not a power of 2", 30 | fun(Size) -> 31 | (Size band (Size-1) =:= 0) 32 | end}. 33 | 34 | {validator, "ring_size_min", "must be at least 8", 35 | fun(Size) -> 36 | Size >= 8 37 | end}. 38 | 39 | %% @doc Number of concurrent node-to-node transfers allowed. 40 | {mapping, "transfer_limit", "riak_core.handoff_concurrency", [ 41 | {datatype, integer}, 42 | {default, 2}, 43 | {commented, 2} 44 | ]}. 45 | 46 | %% @doc Default location of ringstate 47 | {mapping, "ring.state_dir", "riak_core.ring_state_dir", [ 48 | {datatype, directory}, 49 | {default, "$(platform_data_dir)/ring"}, 50 | hidden 51 | ]}. 52 | 53 | %% @doc Default cert location for https can be overridden 54 | %% with the ssl config variable, for example: 55 | {mapping, "ssl.certfile", "riak_core.ssl.certfile", [ 56 | {datatype, file}, 57 | {commented, "$(platform_etc_dir)/cert.pem"} 58 | ]}. 59 | 60 | %% @doc Default key location for https can be overridden with the ssl 61 | %% config variable, for example: 62 | {mapping, "ssl.keyfile", "riak_core.ssl.keyfile", [ 63 | {datatype, file}, 64 | {commented, "$(platform_etc_dir)/key.pem"} 65 | ]}. 66 | 67 | %% @doc Default signing authority location for https can be overridden 68 | %% with the ssl config variable, for example: 69 | {mapping, "ssl.cacertfile", "riak_core.ssl.cacertfile", [ 70 | {datatype, file}, 71 | {commented, "$(platform_etc_dir)/cacertfile.pem"} 72 | ]}. 73 | 74 | %% @doc handoff.port is the TCP port that Riak uses for 75 | %% intra-cluster data handoff. 76 | {mapping, "handoff.port", "riak_core.handoff_port", [ 77 | {default, {{handoff_port}} }, 78 | {datatype, integer}, 79 | hidden 80 | ]}. 81 | 82 | %% @doc To encrypt riak_core intra-cluster data handoff traffic, 83 | %% uncomment the following line and edit its path to an appropriate 84 | %% certfile and keyfile. (This example uses a single file with both 85 | %% items concatenated together.) 86 | {mapping, "handoff.ssl.certfile", "riak_core.handoff_ssl_options.certfile", [ 87 | %% {commented, "/tmp/erlserver.pem"}, 88 | {datatype, file}, 89 | hidden 90 | ]}. 91 | 92 | %% @doc if you need a seperate keyfile for handoff 93 | {mapping, "handoff.ssl.keyfile", "riak_core.handoff_ssl_options.keyfile", [ 94 | {datatype, file}, 95 | hidden 96 | ]}. 97 | 98 | %% @doc Enables/disables outbound handoff transfers for this node. If you 99 | %% turn this setting off at runtime with riak-admin, it will kill any 100 | %% outbound handoffs currently running. 101 | {mapping, "handoff.outbound", "riak_core.disable_outbound_handoff", [ 102 | {default, on}, 103 | {datatype, {flag, off, on}}, 104 | hidden 105 | ]}. 106 | 107 | %% @doc Enables/disables inbound handoff transfers for this node. If you 108 | %% turn this setting off at runtime with riak-admin, it will kill any 109 | %% inbound handoffs currently running. 110 | {mapping, "handoff.inbound", "riak_core.disable_inbound_handoff", [ 111 | {default, on}, 112 | {datatype, {flag, off, on}}, 113 | hidden 114 | ]}. 115 | 116 | %% @doc DTrace support Do not enable 'dtrace' unless your Erlang/OTP 117 | %% runtime is compiled to support DTrace. DTrace is available in 118 | %% R15B01 (supported by the Erlang/OTP official source package) and in 119 | %% R14B04 via a custom source repository & branch. 120 | {mapping, "dtrace", "riak_core.dtrace_support", [ 121 | {default, off}, 122 | {datatype, flag} 123 | ]}. 124 | 125 | %% @doc Platform-specific installation paths (substituted by rebar) 126 | {mapping, "platform_bin_dir", "riak_core.platform_bin_dir", [ 127 | {datatype, directory}, 128 | {default, "{{platform_bin_dir}}"} 129 | ]}. 130 | 131 | %% @see platform_bin_dir 132 | {mapping, "platform_data_dir", "riak_core.platform_data_dir", [ 133 | {datatype, directory}, 134 | {default, "{{platform_data_dir}}"} 135 | ]}. 136 | 137 | %% @see platform_bin_dir 138 | {mapping, "platform_etc_dir", "riak_core.platform_etc_dir", [ 139 | {datatype, directory}, 140 | {default, "{{platform_etc_dir}}"} 141 | ]}. 142 | 143 | %% @see platform_bin_dir 144 | {mapping, "platform_lib_dir", "riak_core.platform_lib_dir", [ 145 | {datatype, directory}, 146 | {default, "{{platform_lib_dir}}"} 147 | ]}. 148 | 149 | %% @see platform_bin_dir 150 | {mapping, "platform_log_dir", "riak_core.platform_log_dir", [ 151 | {datatype, directory}, 152 | {default, "{{platform_log_dir}}"} 153 | ]}. 154 | 155 | %% @doc Enable consensus subsystem. Set to 'on' to enable the 156 | %% consensus subsystem used for strongly consistent Riak operations. 157 | {mapping, "strong_consistency", "riak_core.enable_consensus", [ 158 | {datatype, flag}, 159 | {default, off}, 160 | {commented, on} 161 | ]}. 162 | 163 | %% @doc Whether to enable the background manager globally. When 164 | %% enabled, participating Riak subsystems will coordinate access to 165 | %% shared resources. This will help to prevent system response 166 | %% degradation under times of heavy load from multiple background 167 | %% tasks. Specific subsystems may also have their own controls over 168 | %% use of the background manager. 169 | {mapping, "background_manager", "riak_core.use_background_manager", [ 170 | {datatype, flag}, 171 | {default, off}, 172 | hidden 173 | ]}. 174 | 175 | %% @doc Interval of time between vnode management 176 | %% activities. Modifying this will change the amount of time between 177 | %% attemps to trigger handoff between this node and any other member 178 | %% of the cluster. 179 | {mapping, "vnode_management_timer", "riak_core.vnode_management_timer", [ 180 | {default, "10s"}, 181 | {datatype, {duration, ms}}, 182 | hidden 183 | ]}. 184 | -------------------------------------------------------------------------------- /util/shell_app.config: -------------------------------------------------------------------------------- 1 | [ 2 | {nkdist, [ 3 | ]}, 4 | 5 | {lager, [ 6 | {handlers, [ 7 | {lager_console_backend, info}, 8 | {lager_file_backend, [{file, "log/error.log"}, {level, error}]}, 9 | {lager_file_backend, [{file, "log/console.log"}, {level, info}]} 10 | ]}, 11 | {error_logger_redirect, false}, 12 | {crash_log, "log/crash.log"}, 13 | {colored, true}, 14 | {colors, [ 15 | {debug, "\e[0;38m" }, 16 | {info, "\e[0;32m" }, 17 | {notice, "\e[1;36m" }, 18 | {warning, "\e[1;33m" }, 19 | {error, "\e[1;31m" } 20 | ]} 21 | ]}, 22 | 23 | {riak_core, [ 24 | {schema_dirs, ["util"]}, 25 | % {enable_consensus, true}, 26 | 27 | %% Cluster name 28 | {cluster_name, "default"}, 29 | 30 | %% Default location for ring, cluster and other data files 31 | {platform_data_dir, "data"}, 32 | 33 | %% Default ring creation size. Make sure it is a power of 2, 34 | %% e.g. 16, 32, 64, 128, 256, 512 etc 35 | {ring_creation_size, 8}, 36 | 37 | %% Default gossip interval (milliseconds) 38 | {gossip_interval, 60000}, 39 | 40 | %% Target N value 41 | {target_n_val, 4}, 42 | 43 | % %% Default claims functions 44 | % {wants_claim_fun, {riak_core_claim, default_wants_claim}}, 45 | % {choose_claim_fun, {riak_core_claim, default_choose_claim}}, 46 | 47 | %% Vnode inactivity timeout (how often to check if fallback vnodes 48 | %% should return their data) in ms. 49 | {vnode_inactivity_timeout, 60000}, 50 | 51 | %% Number of VNodes allowed to do handoff concurrently. 52 | {handoff_concurrency, 2} 53 | ]}, 54 | 55 | {sasl, [ 56 | {sasl_error_logger, false} 57 | ]} 58 | ]. 59 | -------------------------------------------------------------------------------- /util/shell_vm.args: -------------------------------------------------------------------------------- 1 | -pa deps/basho_stats/ebin 2 | -pa deps/bear/ebin 3 | -pa deps/clique/ebin 4 | -pa deps/cluster_info/ebin 5 | -pa deps/cuttlefish/ebin 6 | -pa deps/edown/ebin 7 | -pa deps/eleveldb/ebin 8 | -pa deps/eper/ebin 9 | -pa deps/exometer_core/ebin 10 | -pa deps/folsom/ebin 11 | -pa deps/goldrush/ebin 12 | -pa deps/jsx/ebin 13 | -pa deps/lager/ebin 14 | -pa deps/meck/ebin 15 | -pa deps/neotoma/ebin 16 | -pa deps/nklib/ebin 17 | -pa deps/parse_trans/ebin 18 | -pa deps/pbkdf2/ebin 19 | -pa deps/poolboy/ebin 20 | -pa deps/riak_core/ebin 21 | -pa deps/riak_dt/ebin 22 | -pa deps/riak_ensemble/ebin 23 | -pa deps/riak_sysmon/ebin 24 | -pa deps/setup/ebin 25 | -pa deps/sext/ebin 26 | -pa ../nkdist/ebin 27 | 28 | ## Name of the node 29 | -name nkcluster@127.0.0.1 30 | -setcookie nk 31 | 32 | ## More processes 33 | +P 1000000 34 | 35 | ## Treat error_logger warnings as warnings 36 | +W w 37 | 38 | ## Increase number of concurrent ports/sockets 39 | -env ERL_MAX_PORTS 65535 40 | 41 | ## Tweak GC to run more often 42 | #-env ERL_FULLSWEEP_AFTER 0 43 | 44 | ## Set the location of crash dumps 45 | -env ERL_CRASH_DUMP . 46 | --------------------------------------------------------------------------------