summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrad Anderson <brad@cloudant.com>2010-03-15 16:11:33 -0400
committerBrad Anderson <brad@cloudant.com>2010-05-09 22:56:22 -0400
commit8429ee374325a6a2c9779c5001c143ce4b35b1c6 (patch)
tree995b1e2885ca5e8a17d034cb7c50c27482c66dc1
parente522c645e3d34403ae64193db3422ad8c574d256 (diff)
more work on mem3 init, handling different types of joins, requiring more human-intervention, reworking startargs to strip out most everything
-rw-r--r--ebin/dynomite.app1
-rw-r--r--include/common.hrl5
-rw-r--r--src/dynomite_app.erl10
-rw-r--r--src/dynomite_sup.erl39
-rw-r--r--src/mem3.erl100
-rw-r--r--test/mem3_test.erl38
6 files changed, 114 insertions, 79 deletions
diff --git a/ebin/dynomite.app b/ebin/dynomite.app
index b339496a..e6e71af2 100644
--- a/ebin/dynomite.app
+++ b/ebin/dynomite.app
@@ -18,6 +18,7 @@
dynomite_prof,
dynomite_sup,
lib_misc,
+ mem3,
mem_utils,
membership2,
node,
diff --git a/include/common.hrl b/include/common.hrl
index 608a23ad..6d92d9fa 100644
--- a/include/common.hrl
+++ b/include/common.hrl
@@ -44,4 +44,7 @@
-record(mem, {header=3,
node,
nodes,
- clock}).
+ clock,
+ ets,
+ test=false
+ }).
diff --git a/src/dynomite_app.erl b/src/dynomite_app.erl
index b31b2e57..417f4c76 100644
--- a/src/dynomite_app.erl
+++ b/src/dynomite_app.erl
@@ -40,17 +40,9 @@
%% @doc start required apps, join cluster, start dynomite supervisor
start(_Type, _StartArgs) ->
- % get process_dict hack for startargs (i.e. not from .app file)
- PdStartArgs = case erase(startargs) of
- undefined ->
- [];
- Args ->
- Args
- end,
-
% start dynomite supervisor
ok = start_node(),
- case dynomite_sup:start_link(PdStartArgs) of
+ case dynomite_sup:start_link() of
{ok, Supervisor} ->
{ok, Supervisor};
Error ->
diff --git a/src/dynomite_sup.erl b/src/dynomite_sup.erl
index 1bf0de22..b60824ac 100644
--- a/src/dynomite_sup.erl
+++ b/src/dynomite_sup.erl
@@ -1,20 +1,10 @@
-%%%-------------------------------------------------------------------
-%%% File: dynomite_sup.erl
-%%% @author Cliff Moon <cliff@powerset.com> []
-%%% @copyright 2008 Cliff Moon
-%%% @doc
-%%%
-%%% @end
-%%%
-%%% @since 2008-06-27 by Cliff Moon
-%%%-------------------------------------------------------------------
-module(dynomite_sup).
--author('cliff@powerset.com').
+-author('brad@cloudant.com').
-behaviour(supervisor).
%% API
--export([start_link/1]).
+-export([start_link/0]).
%% Supervisor callbacks
-export([init/1]).
@@ -31,8 +21,8 @@
%% @doc Starts the supervisor
%% @end
%%--------------------------------------------------------------------
-start_link(Hints) ->
- supervisor:start_link(?MODULE, [Hints]).
+start_link() ->
+ supervisor:start_link(?MODULE, []).
%%====================================================================
%% Supervisor callbacks
@@ -47,11 +37,9 @@ start_link(Hints) ->
%% specifications.
%% @end
%%--------------------------------------------------------------------
-init(Args) ->
- Node = node(),
- Nodes = running_nodes() ++ [node()],
+init(_Args) ->
Membership = {membership,
- {mem3, start_link, [Node, Nodes, Args]},
+ {mem3, start_link, []},
permanent,
1000,
worker,
@@ -68,18 +56,3 @@ init(Args) ->
%%====================================================================
%% Internal functions
%%====================================================================
-
-%% @doc get a list of running nodes visible to this local node
-running_nodes() ->
- [Node || Node <- nodes([this,visible]), running(Node)].
-
-%% @doc monitor the membership server on Node from here
-running(Node) ->
- Ref = erlang:monitor(process, {membership, Node}),
- R = receive
- {'DOWN', Ref, _, _, _} -> false
- after 1 ->
- true
- end,
- erlang:demonitor(Ref),
- R.
diff --git a/src/mem3.erl b/src/mem3.erl
index 6f53ed23..1018af29 100644
--- a/src/mem3.erl
+++ b/src/mem3.erl
@@ -5,8 +5,8 @@
-behaviour(gen_server).
%% API
--export([start_link/2, start_link/3, stop/0, stop/1]).
--export([clock/0, state/0]).
+-export([start_link/0, start_link/1, stop/0, stop/1]).
+-export([join/2, clock/0, state/0]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
@@ -21,12 +21,12 @@
%% API
%%====================================================================
-start_link(Node, ErlNodes) ->
- start_link(Node, ErlNodes, []).
+start_link() ->
+ start_link([]).
-start_link(Node, ErlNodes, Args) ->
- gen_server:start_link({local, ?MODULE}, ?MODULE, [Node, ErlNodes, Args], []).
+start_link(Args) ->
+ gen_server:start_link({local, ?MODULE}, ?MODULE, Args, []).
stop() ->
@@ -37,6 +37,10 @@ stop(Server) ->
gen_server:cast(Server, stop).
+join(JoinType, Nodes) ->
+ gen_server:call(?MODULE, {join, JoinType, Nodes}).
+
+
clock() ->
gen_server:call(?MODULE, clock).
@@ -50,20 +54,23 @@ state() ->
%%====================================================================
%% start up membership server
-init([Node, Nodes, Args]) ->
+init(Args) ->
process_flag(trap_exit,true),
- showroom_log:message(info, "membership: membership server starting...", []),
- net_kernel:monitor_nodes(true),
- Options = lists:flatten(Args),
Config = configuration:get_config(),
- OldState = read_latest_state_file(Config),
- State = handle_init(Node, Nodes, Options, OldState, Config),
- {ok, State}.
+ OldState = case Args of
+ test -> nil;
+ _ -> read_latest_state_file(Config)
+ end,
+ State = handle_init(OldState),
+ {ok, State#mem{test=(Args == test)}}.
+
%% new node joining to this node
-handle_call({join, _JoiningNode, _Options}, _From, State) ->
- {reply, ok, State};
+handle_call({join, JoinType, ExtNodes}, _From, State) ->
+ Config = configuration:get_config(),
+ Reply = handle_join(JoinType, ExtNodes, State, Config),
+ {reply, Reply, State};
%% clock
handle_call(clock, _From, State = #mem{clock=Clock}) ->
@@ -114,7 +121,7 @@ terminate(_Reason, _State) ->
% ignored code change
code_change(OldVsn, State, _Extra) ->
- io:format("Unknown Old Version!~nOldVsn: ~p~nState : ~p~n", [OldVsn, State]),
+ io:format("Unknown Old Version~nOldVsn: ~p~nState : ~p~n", [OldVsn, State]),
{ok, State}.
@@ -122,35 +129,50 @@ code_change(OldVsn, State, _Extra) ->
%%% Internal functions
%%--------------------------------------------------------------------
-% we could be:
-% 1. starting fresh node into a fresh cluster (we're one of first nodes)
-% 2. starting fresh node into an existing cluster (need to join)
-% 3. rejoining a cluster after some downtime
+% we could be automatically:
+% 1. rejoining a cluster after some downtime
+%
+% we could be manually:
+% 2. beginning a cluster with only this node
+% 3. joining a cluster as a new node
% 4. replacing a node in an existing cluster
-handle_init(Node, [], nil, Options, Config) ->
- % no other erlang nodes, no old state
- Hints = proplists:get_value(hints, Options),
- Map = create_map(Config, [{Node, Hints}]),
- ?debugFmt("~nmap: ~p~n", [Map]);
-
-handle_init(_Node, [], _OldState, _Options, _Config) ->
- % no other erlang nodes, old state
- % network partition?
+handle_init(nil) ->
+ showroom_log:message(info, "membership: membership server starting...", []),
+ net_kernel:monitor_nodes(true),
+ Table = init_ets_table(),
+ Node = node(),
+ Nodes = [{Node, []}],
+ Clock = vector_clock:create(Node),
+ #mem{node=Node, nodes=Nodes, clock=Clock, ets=Table};
+
+handle_init(_OldState) ->
+ ?debugHere,
+ % there's an old state, let's try to rejoin automatically
+ % TODO implement me
+ Table = init_ets_table(),
+ #mem{ets=Table}.
+
+
+%% handle join activities
+handle_join(first, ExtNodes, State, Config) ->
+ Map = create_map(Config, ExtNodes),
+ ?debugFmt("~nmap: ~p~n", [Map]),
+ State#mem{};
+
+handle_join(new, _ExtNodes, _State, _Config) ->
ok;
-handle_init(_Node, _ErlNodes, nil, _Options, _Config) ->
- % other erlang nodes, no old state
+handle_join(replace, [_OldNode | _], _State, _Config) ->
ok;
-handle_init(_Node, _ErlNodes, _OldState, _Options, _Config) ->
- % other erlang nodes, old state
- % network partition?
- ok.
+handle_join(JoinType, _, _, _) ->
+ showroom_log:message(info, "membership: unknown join type: ~p", [JoinType]),
+ {error, {unknown_join_type, JoinType}}.
+%% @doc find the latest state file on disk
find_latest_state_filename(Config) ->
- ?debugFmt("~nConfig: ~p~n", [Config]),
Dir = Config#config.directory,
case file:list_dir(Dir) of
{ok, Filenames} ->
@@ -202,3 +224,9 @@ make_fullmap(PMap) ->
[{Node, Part, primary} | PartnerList]
end, PMap),
NodeParts.
+
+
+init_ets_table() ->
+ Table = list_to_atom(lists:concat(["mem_", atom_to_list(node())])),
+ ets:new(Table, [public, set, named_table]),
+ Table.
diff --git a/test/mem3_test.erl b/test/mem3_test.erl
new file mode 100644
index 00000000..6286936e
--- /dev/null
+++ b/test/mem3_test.erl
@@ -0,0 +1,38 @@
+-module(mem3_test).
+
+-include("../include/common.hrl").
+-include_lib("eunit/include/eunit.hrl").
+
+%% TEST SETUP
+
+all_tests_test_() ->
+ {"membership3 tests",
+ [
+ {setup,
+ fun test_setup/0,
+ fun test_teardown/1,
+ fun(Pid) ->
+ {with, Pid,
+ [
+ fun init/1
+ ]}
+ end}
+ ]
+ }.
+
+
+test_setup() ->
+ {ok, Pid} = mem3:start_link(test),
+ Pid.
+
+
+test_teardown(Pid) ->
+ exit(Pid, shutdown).
+
+
+%% TESTS
+
+init(_Pid) ->
+ State = #mem{test=Test} = mem3:state(),
+ ?debugFmt("~nState: ~p~n", [State]),
+ ?assertEqual(true, Test).