1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
%%%-------------------------------------------------------------------
%%% File: dynomite.erl
%%% @author Cliff Moon <cliff@powerset.com> []
%%% @copyright 2008 Cliff Moon
%%% @doc
%%%
%%% @end
%%%
%%% @since 2008-06-27 by Cliff Moon
%%%-------------------------------------------------------------------
-module(dynomite_app).
-author('cliff@powerset.com').
-author('brad@cloudant.com').
-behaviour(application).
-include("../include/config.hrl").
-include("../../couch/src/couch_db.hrl").
%% Application callbacks
-export([start/2, stop/1]).
-define(DEFAULT_CLUSTER_URL, "http://localhost:5984/_cluster").
%%====================================================================
%% Application callbacks
%%====================================================================
%%--------------------------------------------------------------------
%% @spec start(Type, StartArgs) -> {ok, Pid} |
%% {ok, Pid, State} |
%% {error, Reason}
%% @doc This function is called whenever an application
%% is started using application:start/1,2, and should start the processes
%% of the application. If the application is structured according to the
%% OTP design principles as a supervision tree, this means starting the
%% top supervisor of the tree.
%% @end
%%--------------------------------------------------------------------
%% @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
{ok, Supervisor} ->
{ok, Supervisor};
Error ->
Error
end.
%%--------------------------------------------------------------------
%% @spec stop(State) -> void()
%% @doc This function is called whenever an application
%% has stopped. It is intended to be the opposite of Module:start/2 and
%% should do any necessary cleaning up. The return value is ignored.
%% @end
%%--------------------------------------------------------------------
stop({_, Sup}) ->
showroom_log:message(alert, "dynomite application stopped", []),
exit(Sup, normal),
ok.
%%====================================================================
%% Internal functions
%%====================================================================
%% @spec start_node() -> ok | {error, Reason}
%% @doc start this node (join to dist. erlang cluster)
start_node() ->
PingUrl = couch_config:get("cluster","ping", ?DEFAULT_CLUSTER_URL),
?LOG_DEBUG("PingUrl: ~p", [PingUrl]),
Result = case get_pingnode(PingUrl, 1) of
{ok, PingNode} ->
join(PingNode);
_ ->
?LOG_INFO("No pingnode found. Becoming single-node cluster", [])
end,
couch_api:create_db(<<"users">>, []), % all nodes have local 'users' db
Result.
%% @spec get_pingnode(Url::string(), Retries::int()) -> node() |
%% {error, Reason}
%% @doc make a http get call to Url to get cluster information
get_pingnode(Url, Retries) ->
try couch_rep_httpc:request(#http_db{url=Url, retries=Retries}) of
{[{<<"ping_node">>, Node}]} ->
{ok, list_to_atom(binary_to_list(Node))};
_ ->
{error, pingnode_not_found}
catch
_:_ ->
{error, pingnode_not_found}
end.
join(PingNode) ->
if
node() =:= PingNode ->
ok; % we must be brain, so we'll take over the world
true ->
case net_adm:ping(PingNode) of
pong ->
% there is a cluster, we just joined it
?LOG_DEBUG("ping successful, we're in.", []),
timer:sleep(1000); %% grr, what a hack, erlang. rly?
pang ->
?LOG_ERROR("ping not successful.", []),
throw({cluster_error, ?l2b("cluster ping not successful")})
end
end,
ok.
|