You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ja...@apache.org on 2017/10/08 16:12:51 UTC
[couchdb] branch 749-fix-couch_peruser-app-structure updated
(ede00a6 -> 8b9cc5e)
This is an automated email from the ASF dual-hosted git repository.
jan pushed a change to branch 749-fix-couch_peruser-app-structure
in repository https://gitbox.apache.org/repos/asf/couchdb.git.
omit ede00a6 fix tests
omit 73adae3 add type specs
omit 53a53b9 make sure peruser listeners are only initialised once per node
omit baab400 remove reliance on couch_replicator_clustering, handle cluster state internally
omit e749b7a move couch_replication_clustering:owner/3 to mem3.erl
omit 4023125 track cluster state in gen_server state and get notfied from mem3 directly
omit 5c39bee Ensure a user creation is handlined on one node only
omit 8dd21de feat: mango test runner: do not rely on timeout for CouchDB start alone
omit 38d0f85 Start and stop couch_peruser in the test suite
omit 5f600b4 Make couch_peruser a proper Erlang app
add 4c61d10 Fix attachments tests for 20.0 compatibility
add c26ce7f Unit tests now pass on 20.0. Eenable it in rebar.config and in Travis
new b66e2ef Make couch_peruser a proper Erlang app
new b975e24 Start and stop couch_peruser in the test suite
new 18f558c feat: mango test runner: do not rely on timeout for CouchDB start alone
new a29e164 Ensure a user creation is handlined on one node only
new b10249b track cluster state in gen_server state and get notfied from mem3 directly
new a35c53b move couch_replication_clustering:owner/3 to mem3.erl
new 0400ca5 remove reliance on couch_replicator_clustering, handle cluster state internally
new 736c269 make sure peruser listeners are only initialised once per node
new 6203e08 add type specs
new 8b9cc5e fix tests
This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version. This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:
* -- * -- B -- O -- O -- O (ede00a6)
\
N -- N -- N refs/heads/749-fix-couch_peruser-app-structure (8b9cc5e)
You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.
Any revisions marked "omit" are not gone; other references still
refer to them. Any revisions marked "discard" are gone forever.
The 10 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.travis.yml | 1 +
rebar.config.script | 2 +-
src/couch/test/couchdb_attachments_tests.erl | 4 ++--
3 files changed, 4 insertions(+), 3 deletions(-)
--
To stop receiving notification emails like this one, please contact
['"commits@couchdb.apache.org" <co...@couchdb.apache.org>'].
[couchdb] 07/10: remove reliance on couch_replicator_clustering,
handle cluster state internally
Posted by ja...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
jan pushed a commit to branch 749-fix-couch_peruser-app-structure
in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit 0400ca548e5ef6eea9380fbeafe095a70bdb0433
Author: Jan Lehnardt <ja...@apache.org>
AuthorDate: Sun Oct 8 11:14:28 2017 +0200
remove reliance on couch_replicator_clustering, handle cluster state internally
---
src/couch_peruser/src/couch_peruser.erl | 56 ++++++++++++++++++++++-----------
1 file changed, 37 insertions(+), 19 deletions(-)
diff --git a/src/couch_peruser/src/couch_peruser.erl b/src/couch_peruser/src/couch_peruser.erl
index a31ff60..791431c 100644
--- a/src/couch_peruser/src/couch_peruser.erl
+++ b/src/couch_peruser/src/couch_peruser.erl
@@ -33,7 +33,8 @@
]).
-record(state, {parent, db_name, delete_dbs, changes_pid, changes_ref}).
--record(clusterState, {parent,
+-record(clusterState, {
+ parent,
db_name,
delete_dbs,
states,
@@ -48,10 +49,10 @@
start_link() ->
- gen_server:start_link(?MODULE, [], []).
+ gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
init() ->
- couch_log:debug("peruser: starting on node ~p", [node()]),
+ couch_log:debug("peruser: starting on node ~p in pid ~p", [node(), self()]),
case config:get_boolean("couch_peruser", "enable", false) of
false ->
couch_log:debug("peruser: disabled on node ~p", [node()]),
@@ -107,6 +108,8 @@ start_listening(#clusterState{db_name=DbName, delete_dbs=DeleteDbs} = ClusterSta
end.
init_changes_handler(#state{db_name=DbName} = State) ->
+ % leave for debugging
+ % couch_log:debug("peruser: init_changes_handler() on DbName ~p", [DbName]),
try
{ok, Db} = couch_db:open_int(DbName, [?ADMIN_CTX, sys_db]),
FunAcc = {fun ?MODULE:changes_handler/3, State},
@@ -120,6 +123,9 @@ init_changes_handler(#state{db_name=DbName} = State) ->
changes_handler({change, {Doc}, _Prepend}, _ResType, State=#state{db_name=DbName}) ->
+ % leave for debugging
+ % couch_log:debug("peruser: changes_handler() on DbName/Doc ~p/~p", [DbName, Doc]),
+
case couch_util:get_value(<<"id">>, Doc) of
<<"org.couchdb.user:",User/binary>>=DocId ->
case should_handle_doc(DbName, DocId) of
@@ -149,22 +155,28 @@ changes_handler({change, {Doc}, _Prepend}, _ResType, State=#state{db_name=DbName
changes_handler(_Event, _ResType, State) ->
State.
-should_handle_doc(DbName, DocId) ->
- case couch_replicator_clustering:owner(DbName, DocId) of
- unstable ->
- % todo: when we do proper resume[1], we can return false here
- % and rely on a module restart when the cluster is stable again
- % in the meantime, we risk conflicts when the cluster gets unstable
- % and users are being created.
- % [1] https://github.com/apache/couchdb/issues/872
- true;
- ThisNode when ThisNode =:= node() ->
- couch_log:debug("peruser: handling ~s/~s", [DbName, DocId]),
- % do the deed
- true;
- _OtherNode ->
- couch_log:debug("peruser: skipping ~s/~s", [DbName, DocId]),
- false
+should_handle_doc(ShardName, DocId) ->
+ should_handle_doc_int(ShardName, DocId, is_stable()).
+
+should_handle_doc_int(ShardName, DocId, false) ->
+ % when the cluster is unstable, we have already stopped all Listeners
+ % the next stable event will restart all listeners and pick up this
+ % doc change
+ couch_log:debug("peruser: skipping, cluster unstable ~s/~s", [ShardName, DocId]),
+ false;
+should_handle_doc_int(ShardName, DocId, true) ->
+ DbName = mem3:dbname(ShardName),
+ Live = [erlang:node() | erlang:nodes()],
+ Shards = mem3:shards(DbName, DocId),
+ Nodes = [N || #shard{node=N} <- Shards, lists:member(N, Live)],
+ case mem3:owner(DbName, DocId, Nodes) of
+ ThisNode when ThisNode =:= node() ->
+ couch_log:debug("peruser: handling ~s/~s", [DbName, DocId]),
+ % do the deed
+ true;
+ _OtherNode ->
+ couch_log:debug("peruser: skipping ~s/~s", [DbName, DocId]),
+ false
end.
@@ -258,6 +270,10 @@ exit_changes(ClusterState) ->
exit(State#state.changes_pid, kill)
end, ClusterState#clusterState.states).
+-spec is_stable() -> true | false.
+is_stable() ->
+ gen_server:call(?MODULE, is_stable).
+
% Mem3 cluster callbacks
cluster_unstable(Server) ->
@@ -274,6 +290,8 @@ init([]) ->
ok = subscribe_for_changes(),
{ok, init()}.
+handle_call(is_stable, _From, #clusterState{cluster_stable = IsStable} = State) ->
+ {reply, IsStable, State};
handle_call(_Msg, _From, State) ->
{reply, error, State}.
--
To stop receiving notification emails like this one, please contact
"commits@couchdb.apache.org" <co...@couchdb.apache.org>.
[couchdb] 10/10: fix tests
Posted by ja...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
jan pushed a commit to branch 749-fix-couch_peruser-app-structure
in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit 8b9cc5efc19d336bd54404b4479d80d8697500fe
Author: Jan Lehnardt <ja...@apache.org>
AuthorDate: Sun Oct 8 16:54:17 2017 +0200
fix tests
---
src/couch_peruser/src/couch_peruser.app.src | 2 +-
src/couch_peruser/test/couch_peruser_test.erl | 17 +++++++++++++++++
2 files changed, 18 insertions(+), 1 deletion(-)
diff --git a/src/couch_peruser/src/couch_peruser.app.src b/src/couch_peruser/src/couch_peruser.app.src
index 42b7b25..9859e03 100644
--- a/src/couch_peruser/src/couch_peruser.app.src
+++ b/src/couch_peruser/src/couch_peruser.app.src
@@ -14,7 +14,7 @@
{description, "couch_peruser - maintains per-user databases in CouchDB"},
{vsn, git},
{registered, []},
- {applications, [kernel, stdlib, config, couch, fabric, couch_replicator, mem3]},
+ {applications, [kernel, stdlib, config, couch, fabric, mem3]},
{mod, {couch_peruser_app, []}},
{env, []},
{modules, [couch_peruser, couch_peruser_app, couch_peruser_sup]}
diff --git a/src/couch_peruser/test/couch_peruser_test.erl b/src/couch_peruser/test/couch_peruser_test.erl
index f7ef8cd..726b2db 100644
--- a/src/couch_peruser/test/couch_peruser_test.erl
+++ b/src/couch_peruser/test/couch_peruser_test.erl
@@ -35,13 +35,22 @@ setup() ->
do_request(put, get_base_url() ++ "/" ++ ?b2l(TestAuthDb)),
do_request(put, get_cluster_base_url() ++ "/" ++ ?b2l(TestAuthDb)),
set_config("couch_httpd_auth", "authentication_db", ?b2l(TestAuthDb)),
+ set_config("couch_peruser", "cluster_quiet_period", "1"),
+ set_config("couch_peruser", "cluster_start_period", "1"),
set_config("couch_peruser", "enable", "true"),
+ set_config("cluster", "n", "1"),
+ set_config("log", "level", "debug"),
+ timer:sleep(6000),
TestAuthDb.
teardown(TestAuthDb) ->
set_config("couch_peruser", "enable", "false"),
set_config("couch_peruser", "delete_dbs", "false"),
set_config("couch_httpd_auth", "authentication_db", "_users"),
+ set_config("couch_peruser", "cluster_quiet_period", "60"),
+ set_config("couch_peruser", "cluster_start_period", "5"),
+ set_config("cluster", "n", "3"),
+ set_config("log", "level", "info"),
do_request(delete, get_cluster_base_url() ++ "/" ++ ?b2l(TestAuthDb)),
do_request(delete, get_base_url() ++ "/" ++ ?b2l(TestAuthDb)),
lists:foreach(fun (DbName) ->
@@ -153,8 +162,10 @@ should_delete_user_db(TestAuthDb) ->
UserDbName = <<"userdb-626172">>,
set_config("couch_peruser", "delete_dbs", "true"),
create_user(TestAuthDb, User),
+ timer:sleep(2000),
?assert(lists:member(UserDbName, all_dbs())),
delete_user(TestAuthDb, User),
+ timer:sleep(2000),
?_assert(not lists:member(UserDbName, all_dbs())).
should_reflect_config_changes(TestAuthDb) ->
@@ -162,20 +173,26 @@ should_reflect_config_changes(TestAuthDb) ->
UserDbName = <<"userdb-62617a">>,
set_config("couch_peruser", "delete_dbs", "true"),
create_user(TestAuthDb, User),
+ timer:sleep(2000),
?assert(lists:member(UserDbName, all_dbs())),
delete_user(TestAuthDb, User),
+ timer:sleep(2000),
?assert(not lists:member(UserDbName, all_dbs())),
create_user(TestAuthDb, User),
+ timer:sleep(2000),
?assert(lists:member(UserDbName, all_dbs())),
set_config("couch_peruser", "delete_dbs", "false"),
delete_user(TestAuthDb, User),
+ timer:sleep(2000),
?assert(lists:member(UserDbName, all_dbs())),
create_user(TestAuthDb, User),
set_config("couch_peruser", "delete_dbs", "true"),
delete_user(TestAuthDb, User),
+ timer:sleep(2000),
?assert(not lists:member(UserDbName, all_dbs())),
set_config("couch_peruser", "enable", "false"),
create_user(TestAuthDb, User),
+ timer:sleep(2000),
?_assert(not lists:member(UserDbName, all_dbs())).
should_add_user_to_db_admins(TestAuthDb) ->
--
To stop receiving notification emails like this one, please contact
"commits@couchdb.apache.org" <co...@couchdb.apache.org>.
[couchdb] 02/10: Start and stop couch_peruser in the test suite
Posted by ja...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
jan pushed a commit to branch 749-fix-couch_peruser-app-structure
in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit b975e241cfa30896674dd409cbcb1323edb567ab
Author: Russell Branca <ch...@apache.org>
AuthorDate: Thu Aug 17 19:40:38 2017 +0000
Start and stop couch_peruser in the test suite
---
src/couch_peruser/test/couch_peruser_test.erl | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/couch_peruser/test/couch_peruser_test.erl b/src/couch_peruser/test/couch_peruser_test.erl
index c6fde03..f7ef8cd 100644
--- a/src/couch_peruser/test/couch_peruser_test.erl
+++ b/src/couch_peruser/test/couch_peruser_test.erl
@@ -20,12 +20,14 @@
setup_all() ->
TestCtx = test_util:start_couch([chttpd]),
+ ok = application:start(couch_peruser),
Hashed = couch_passwords:hash_admin_password(?ADMIN_PASSWORD),
ok = config:set("admins", ?ADMIN_USERNAME, ?b2l(Hashed), _Persist=false),
TestCtx.
teardown_all(TestCtx) ->
config:delete("admins", ?ADMIN_USERNAME),
+ ok = application:stop(couch_peruser),
test_util:stop_couch(TestCtx).
setup() ->
--
To stop receiving notification emails like this one, please contact
"commits@couchdb.apache.org" <co...@couchdb.apache.org>.
[couchdb] 04/10: Ensure a user creation is handlined on one node
only
Posted by ja...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
jan pushed a commit to branch 749-fix-couch_peruser-app-structure
in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit a29e1646e610ac2b210ea4a55c38c3159f0b82bb
Author: Jan Lehnardt <ja...@apache.org>
AuthorDate: Sat Oct 7 17:04:54 2017 +0200
Ensure a user creation is handlined on one node only
This patch makes use of the mechanism that ensures that replications
are only run on one node.
When the cluster has nodes added/removed all changes listeners are
restarted.
---
src/couch_peruser/src/couch_peruser.app.src | 2 +-
src/couch_peruser/src/couch_peruser.erl | 158 +++++++++++++++++++---------
2 files changed, 110 insertions(+), 50 deletions(-)
diff --git a/src/couch_peruser/src/couch_peruser.app.src b/src/couch_peruser/src/couch_peruser.app.src
index 777446d..42b7b25 100644
--- a/src/couch_peruser/src/couch_peruser.app.src
+++ b/src/couch_peruser/src/couch_peruser.app.src
@@ -14,7 +14,7 @@
{description, "couch_peruser - maintains per-user databases in CouchDB"},
{vsn, git},
{registered, []},
- {applications, [kernel, stdlib, config, couch, fabric]},
+ {applications, [kernel, stdlib, config, couch, fabric, couch_replicator, mem3]},
{mod, {couch_peruser_app, []}},
{env, []},
{modules, [couch_peruser, couch_peruser_app, couch_peruser_sup]}
diff --git a/src/couch_peruser/src/couch_peruser.erl b/src/couch_peruser/src/couch_peruser.erl
index 63ef084..9161f56 100644
--- a/src/couch_peruser/src/couch_peruser.erl
+++ b/src/couch_peruser/src/couch_peruser.erl
@@ -22,6 +22,9 @@
-export([start_link/0, init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
+% cluster state notification callback
+-export([notify_cluster_event/2]).
+
-export([init_changes_handler/1, changes_handler/3]).
-record(state, {parent, db_name, delete_dbs, changes_pid, changes_ref}).
@@ -34,10 +37,13 @@ start_link() ->
gen_server:start_link(?MODULE, [], []).
init() ->
+ couch_log:debug("peruser: starting on node ~p", [node()]),
case config:get_boolean("couch_peruser", "enable", false) of
false ->
+ couch_log:debug("peruser: disabled on node ~p", [node()]),
#clusterState{};
true ->
+ couch_log:debug("peruser: enabled on node ~p", [node()]),
DbName = ?l2b(config:get(
"couch_httpd_auth", "authentication_db", "_users")),
DeleteDbs = config:get_boolean("couch_peruser", "delete_dbs", false),
@@ -47,21 +53,37 @@ init() ->
db_name = DbName,
delete_dbs = DeleteDbs
},
- try
- States = lists:map(fun (A) ->
- S = #state{parent = ClusterState#clusterState.parent,
- db_name = A#shard.name,
- delete_dbs = DeleteDbs},
- {Pid, Ref} = spawn_opt(
- ?MODULE, init_changes_handler, [S], [link, monitor]),
- S#state{changes_pid=Pid, changes_ref=Ref}
- end, mem3:local_shards(DbName)),
-
- ClusterState#clusterState{states = States}
- catch error:database_does_not_exist ->
- couch_log:warning("couch_peruser can't proceed as underlying database (~s) is missing, disables itself.", [DbName]),
- config:set("couch_peruser", "enable", "false", lists:concat([binary_to_list(DbName), " is missing"]))
- end
+
+ % set up cluster-stable listener
+ couch_replicator_clustering:link_cluster_event_listener(?MODULE,
+ notify_cluster_event, [self()]),
+
+ couch_log:debug("peruser: registered for cluster event on node ~p", [node()]),
+ ClusterState
+ end.
+
+% Cluster membership change notification callback
+-spec notify_cluster_event(pid(), {cluster, any()}) -> ok.
+notify_cluster_event(Server, {cluster, _} = Event) ->
+ couch_log:debug("peruser: received cluster event ~p on node ~p", [Event, node()]),
+ gen_server:cast(Server, Event).
+
+start_listening(#clusterState{db_name=DbName, delete_dbs=DeleteDbs} = ClusterState) ->
+ couch_log:debug("peruser: start_listening() on node ~p", [node()]),
+ try
+ States = lists:map(fun (A) ->
+ S = #state{parent = ClusterState#clusterState.parent,
+ db_name = A#shard.name,
+ delete_dbs = DeleteDbs},
+ {Pid, Ref} = spawn_opt(
+ ?MODULE, init_changes_handler, [S], [link, monitor]),
+ S#state{changes_pid=Pid, changes_ref=Ref}
+ end, mem3:local_shards(DbName)),
+
+ ClusterState#clusterState{states = States}
+ catch error:database_does_not_exist ->
+ couch_log:warning("couch_peruser can't proceed as underlying database (~s) is missing, disables itself.", [DbName]),
+ config:set("couch_peruser", "enable", "false", lists:concat([binary_to_list(DbName), " is missing"]))
end.
init_changes_handler(#state{db_name=DbName} = State) ->
@@ -76,24 +98,30 @@ init_changes_handler(#state{db_name=DbName} = State) ->
ok
end.
-changes_handler({change, {Doc}, _Prepend}, _ResType, State=#state{}) ->
+
+changes_handler({change, {Doc}, _Prepend}, _ResType, State=#state{db_name=DbName}) ->
case couch_util:get_value(<<"id">>, Doc) of
- <<"org.couchdb.user:",User/binary>> ->
- case couch_util:get_value(<<"deleted">>, Doc, false) of
- false ->
- UserDb = ensure_user_db(User),
- ok = ensure_security(User, UserDb, fun add_user/3),
- State;
+ <<"org.couchdb.user:",User/binary>>=DocId ->
+ case should_handle_doc(DbName, DocId) of
true ->
- case State#state.delete_dbs of
- true ->
- _UserDb = delete_user_db(User),
- State;
+ case couch_util:get_value(<<"deleted">>, Doc, false) of
false ->
- UserDb = user_db_name(User),
- ok = ensure_security(User, UserDb, fun remove_user/3),
- State
- end
+ UserDb = ensure_user_db(User),
+ ok = ensure_security(User, UserDb, fun add_user/3),
+ State;
+ true ->
+ case State#state.delete_dbs of
+ true ->
+ _UserDb = delete_user_db(User),
+ State;
+ false ->
+ UserDb = user_db_name(User),
+ ok = ensure_security(User, UserDb, fun remove_user/3),
+ State
+ end
+ end;
+ false ->
+ State
end;
_ ->
State
@@ -101,6 +129,25 @@ changes_handler({change, {Doc}, _Prepend}, _ResType, State=#state{}) ->
changes_handler(_Event, _ResType, State) ->
State.
+should_handle_doc(DbName, DocId) ->
+ case couch_replicator_clustering:owner(DbName, DocId) of
+ unstable ->
+ % todo: when we do proper resume[1], we can return false here
+ % and rely on a module restart when the cluster is stable again
+ % in the meantime, we risk conflicts when the cluster gets unstable
+ % and users are being created.
+ % [1] https://github.com/apache/couchdb/issues/872
+ true;
+ ThisNode when ThisNode =:= node() ->
+ couch_log:debug("peruser: handling ~s/~s", [DbName, DocId]),
+ % do the deed
+ true;
+ _OtherNode ->
+ couch_log:debug("peruser: skipping ~s/~s", [DbName, DocId]),
+ false
+ end.
+
+
delete_user_db(User) ->
UserDb = user_db_name(User),
try
@@ -158,20 +205,25 @@ remove_user(User, Prop, {Modified, SecProps}) ->
end.
ensure_security(User, UserDb, TransformFun) ->
- {ok, Shards} = fabric:get_all_security(UserDb, [?ADMIN_CTX]),
- {_ShardInfo, {SecProps}} = hd(Shards),
- % assert that shards have the same security object
- true = lists:all(fun ({_, {SecProps1}}) ->
- SecProps =:= SecProps1
- end, Shards),
- case lists:foldl(
- fun (Prop, SAcc) -> TransformFun(User, Prop, SAcc) end,
- {false, SecProps},
- [<<"admins">>, <<"members">>]) of
- {false, _} ->
- ok;
- {true, SecProps1} ->
- ok = fabric:set_security(UserDb, {SecProps1}, [?ADMIN_CTX])
+ case fabric:get_all_security(UserDb, [?ADMIN_CTX]) of
+ {error, no_majority} ->
+ % single node, ignore
+ ok;
+ {ok, Shards} ->
+ {_ShardInfo, {SecProps}} = hd(Shards),
+ % assert that shards have the same security object
+ true = lists:all(fun ({_, {SecProps1}}) ->
+ SecProps =:= SecProps1
+ end, Shards),
+ case lists:foldl(
+ fun (Prop, SAcc) -> TransformFun(User, Prop, SAcc) end,
+ {false, SecProps},
+ [<<"admins">>, <<"members">>]) of
+ {false, _} ->
+ ok;
+ {true, SecProps1} ->
+ ok = fabric:set_security(UserDb, {SecProps1}, [?ADMIN_CTX])
+ end
end.
user_db_name(User) ->
@@ -179,6 +231,11 @@ user_db_name(User) ->
[string:to_lower(integer_to_list(X, 16)) || <<X>> <= User]),
<<?USERDB_PREFIX,HexUser/binary>>.
+exit_changes(ClusterState) ->
+ lists:foreach(fun (State) ->
+ demonitor(State#state.changes_ref, [flush]),
+ exit(State#state.changes_pid, kill)
+ end, ClusterState#clusterState.states).
%% gen_server callbacks
@@ -191,16 +248,19 @@ handle_call(_Msg, _From, State) ->
handle_cast(update_config, ClusterState) when ClusterState#clusterState.states =/= undefined ->
- lists:foreach(fun (State) ->
- demonitor(State#state.changes_ref, [flush]),
- exit(State#state.changes_pid, kill)
- end, ClusterState#clusterState.states),
-
+ exit_changes(ClusterState),
{noreply, init()};
handle_cast(update_config, _) ->
{noreply, init()};
handle_cast(stop, State) ->
{stop, normal, State};
+handle_cast({cluster, unstable}, ClusterState) when ClusterState#clusterState.states =/= undefined ->
+ exit_changes(ClusterState),
+ {noreply, init()};
+handle_cast({cluster, unstable}, _) ->
+ {noreply, init()};
+handle_cast({cluster, stable}, State) ->
+ {noreply, start_listening(State)};
handle_cast(_Msg, State) ->
{noreply, State}.
--
To stop receiving notification emails like this one, please contact
"commits@couchdb.apache.org" <co...@couchdb.apache.org>.
[couchdb] 06/10: move couch_replication_clustering:owner/3 to
mem3.erl
Posted by ja...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
jan pushed a commit to branch 749-fix-couch_peruser-app-structure
in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit a35c53bc8023aedeb2fd207f8cc371e8cf9e97a4
Author: Jan Lehnardt <ja...@apache.org>
AuthorDate: Sat Oct 7 23:24:14 2017 +0200
move couch_replication_clustering:owner/3 to mem3.erl
---
src/couch_replicator/src/couch_replicator.erl | 2 +-
src/couch_replicator/src/couch_replicator_clustering.erl | 10 +---------
src/mem3/src/mem3.erl | 8 +++++++-
3 files changed, 9 insertions(+), 11 deletions(-)
diff --git a/src/couch_replicator/src/couch_replicator.erl b/src/couch_replicator/src/couch_replicator.erl
index c67b37d..8b7cd5c 100644
--- a/src/couch_replicator/src/couch_replicator.erl
+++ b/src/couch_replicator/src/couch_replicator.erl
@@ -184,7 +184,7 @@ active_doc(DbName, DocId) ->
Live = [node() | nodes()],
Nodes = lists:usort([N || #shard{node=N} <- Shards,
lists:member(N, Live)]),
- Owner = couch_replicator_clustering:owner(DbName, DocId, Nodes),
+ Owner = mem3:owner(DbName, DocId, Nodes),
case active_doc_rpc(DbName, DocId, [Owner]) of
{ok, DocInfo} ->
{ok, DocInfo};
diff --git a/src/couch_replicator/src/couch_replicator_clustering.erl b/src/couch_replicator/src/couch_replicator_clustering.erl
index ed01465..3d5229b 100644
--- a/src/couch_replicator/src/couch_replicator_clustering.erl
+++ b/src/couch_replicator/src/couch_replicator_clustering.erl
@@ -45,7 +45,6 @@
-export([
owner/2,
- owner/3,
is_stable/0,
link_cluster_event_listener/3
]).
@@ -96,13 +95,6 @@ owner(_DbName, _DocId) ->
node().
-% Direct calculation of node membership. This is the algorithm part. It
-% doesn't read the shard map, just picks owner based on a hash.
--spec owner(binary(), binary(), [node()]) -> node().
-owner(DbName, DocId, Nodes) ->
- hd(mem3_util:rotate_list({DbName, DocId}, lists:usort(Nodes))).
-
-
-spec is_stable() -> true | false.
is_stable() ->
gen_server:call(?MODULE, is_stable).
@@ -200,4 +192,4 @@ owner_int(ShardName, DocId) ->
Live = [node() | nodes()],
Shards = mem3:shards(DbName, DocId),
Nodes = [N || #shard{node=N} <- Shards, lists:member(N, Live)],
- owner(DbName, DocId, Nodes).
+ mem3:owner(DbName, DocId, Nodes).
diff --git a/src/mem3/src/mem3.erl b/src/mem3/src/mem3.erl
index e2cbb2e..047154a 100644
--- a/src/mem3/src/mem3.erl
+++ b/src/mem3/src/mem3.erl
@@ -19,7 +19,7 @@
-export([compare_nodelists/0, compare_shards/1]).
-export([quorum/1, group_by_proximity/1]).
-export([live_shards/2]).
--export([belongs/2]).
+-export([belongs/2, owner/3]).
-export([get_placement/1]).
%% For mem3 use only.
@@ -311,6 +311,12 @@ name(#shard{name=Name}) ->
name(#ordered_shard{name=Name}) ->
Name.
+% Direct calculation of node membership. This is the algorithm part. It
+% doesn't read the shard map, just picks owner based on a hash.
+-spec owner(binary(), binary(), [node()]) -> node().
+owner(DbName, DocId, Nodes) ->
+ hd(mem3_util:rotate_list({DbName, DocId}, lists:usort(Nodes))).
+
-ifdef(TEST).
--
To stop receiving notification emails like this one, please contact
"commits@couchdb.apache.org" <co...@couchdb.apache.org>.
[couchdb] 01/10: Make couch_peruser a proper Erlang app
Posted by ja...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
jan pushed a commit to branch 749-fix-couch_peruser-app-structure
in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit b66e2ef060eff7b6868246e6f2d71664cb5f2ec2
Author: Russell Branca <ch...@apache.org>
AuthorDate: Thu Aug 17 17:21:35 2017 +0000
Make couch_peruser a proper Erlang app
---
rel/overlay/etc/default.ini | 1 -
src/couch_peruser/src/couch_peruser.app.src | 5 ++++-
...couch_peruser.app.src => couch_peruser_app.erl} | 22 +++++++++++++------
...couch_peruser.app.src => couch_peruser_sup.erl} | 25 ++++++++++++++++------
4 files changed, 37 insertions(+), 16 deletions(-)
diff --git a/rel/overlay/etc/default.ini b/rel/overlay/etc/default.ini
index 1228535..56f9147 100644
--- a/rel/overlay/etc/default.ini
+++ b/rel/overlay/etc/default.ini
@@ -254,7 +254,6 @@ uuids={couch_uuids, start, []}
auth_cache={couch_auth_cache, start_link, []}
os_daemons={couch_os_daemons, start_link, []}
compaction_daemon={couch_compaction_daemon, start_link, []}
-couch_peruser={couch_peruser, start_link, []}
[mango]
; Set to true to disable the "index all fields" text index, which can lead
diff --git a/src/couch_peruser/src/couch_peruser.app.src b/src/couch_peruser/src/couch_peruser.app.src
index fb6d45b..777446d 100644
--- a/src/couch_peruser/src/couch_peruser.app.src
+++ b/src/couch_peruser/src/couch_peruser.app.src
@@ -14,5 +14,8 @@
{description, "couch_peruser - maintains per-user databases in CouchDB"},
{vsn, git},
{registered, []},
- {applications, [kernel, stdlib, config, couch, fabric]}
+ {applications, [kernel, stdlib, config, couch, fabric]},
+ {mod, {couch_peruser_app, []}},
+ {env, []},
+ {modules, [couch_peruser, couch_peruser_app, couch_peruser_sup]}
]}.
diff --git a/src/couch_peruser/src/couch_peruser.app.src b/src/couch_peruser/src/couch_peruser_app.erl
similarity index 65%
copy from src/couch_peruser/src/couch_peruser.app.src
copy to src/couch_peruser/src/couch_peruser_app.erl
index fb6d45b..770c082 100644
--- a/src/couch_peruser/src/couch_peruser.app.src
+++ b/src/couch_peruser/src/couch_peruser_app.erl
@@ -2,7 +2,7 @@
% use this file except in compliance with the License. You may obtain a copy of
% the License at
%
-% http://www.apache.org/licenses/LICENSE-2.0
+% http://www.apache.org/licenses/LICENSE-2.0
%
% Unless required by applicable law or agreed to in writing, software
% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
@@ -10,9 +10,17 @@
% License for the specific language governing permissions and limitations under
% the License.
-{application, couch_peruser, [
- {description, "couch_peruser - maintains per-user databases in CouchDB"},
- {vsn, git},
- {registered, []},
- {applications, [kernel, stdlib, config, couch, fabric]}
-]}.
+-module(couch_peruser_app).
+
+-behaviour(application).
+
+-export([start/2, stop/1]).
+
+
+start(_Type, _StartArgs) ->
+ couch_peruser_sup:start_link().
+
+
+stop(_State) ->
+ ok.
+
diff --git a/src/couch_peruser/src/couch_peruser.app.src b/src/couch_peruser/src/couch_peruser_sup.erl
similarity index 53%
copy from src/couch_peruser/src/couch_peruser.app.src
copy to src/couch_peruser/src/couch_peruser_sup.erl
index fb6d45b..b89a363 100644
--- a/src/couch_peruser/src/couch_peruser.app.src
+++ b/src/couch_peruser/src/couch_peruser_sup.erl
@@ -2,7 +2,7 @@
% use this file except in compliance with the License. You may obtain a copy of
% the License at
%
-% http://www.apache.org/licenses/LICENSE-2.0
+% http://www.apache.org/licenses/LICENSE-2.0
%
% Unless required by applicable law or agreed to in writing, software
% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
@@ -10,9 +10,20 @@
% License for the specific language governing permissions and limitations under
% the License.
-{application, couch_peruser, [
- {description, "couch_peruser - maintains per-user databases in CouchDB"},
- {vsn, git},
- {registered, []},
- {applications, [kernel, stdlib, config, couch, fabric]}
-]}.
+-module(couch_peruser_sup).
+
+-behaviour(supervisor).
+
+-export([start_link/0, init/1]).
+
+%% Helper macro for declaring children of supervisor
+-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}).
+
+
+start_link() ->
+ supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+
+
+init([]) ->
+ {ok, { {one_for_one, 5, 10}, [?CHILD(couch_peruser, worker)]}}.
+
--
To stop receiving notification emails like this one, please contact
"commits@couchdb.apache.org" <co...@couchdb.apache.org>.
[couchdb] 05/10: track cluster state in gen_server state and get
notfied from mem3 directly
Posted by ja...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
jan pushed a commit to branch 749-fix-couch_peruser-app-structure
in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit b10249bc7aeb385adc8ef8903f47ca38a59be1bb
Author: Jan Lehnardt <ja...@apache.org>
AuthorDate: Sat Oct 7 23:17:30 2017 +0200
track cluster state in gen_server state and get notfied from mem3 directly
---
src/couch_peruser/src/couch_peruser.erl | 65 ++++++++++++++++++++++++---------
1 file changed, 48 insertions(+), 17 deletions(-)
diff --git a/src/couch_peruser/src/couch_peruser.erl b/src/couch_peruser/src/couch_peruser.erl
index 9161f56..a31ff60 100644
--- a/src/couch_peruser/src/couch_peruser.erl
+++ b/src/couch_peruser/src/couch_peruser.erl
@@ -12,12 +12,11 @@
-module(couch_peruser).
-behaviour(gen_server).
+-behaviour(mem3_cluster).
-include_lib("couch/include/couch_db.hrl").
-include_lib("mem3/include/mem3.hrl").
--define(USERDB_PREFIX, "userdb-").
-
% gen_server callbacks
-export([start_link/0, init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
@@ -27,10 +26,25 @@
-export([init_changes_handler/1, changes_handler/3]).
+% mem3_cluster callbacks
+-export([
+ cluster_stable/1,
+ cluster_unstable/1
+]).
+
-record(state, {parent, db_name, delete_dbs, changes_pid, changes_ref}).
--record(clusterState, {parent, db_name, delete_dbs, states}).
+-record(clusterState, {parent,
+ db_name,
+ delete_dbs,
+ states,
+ mem3_cluster_pid,
+ cluster_stable
+}).
+-define(USERDB_PREFIX, "userdb-").
-define(RELISTEN_DELAY, 5000).
+-define(DEFAULT_QUIET_PERIOD, 60). % seconds
+-define(DEFAULT_START_PERIOD, 5). % seconds
start_link() ->
@@ -48,18 +62,24 @@ init() ->
"couch_httpd_auth", "authentication_db", "_users")),
DeleteDbs = config:get_boolean("couch_peruser", "delete_dbs", false),
- ClusterState = #clusterState{
- parent = self(),
- db_name = DbName,
- delete_dbs = DeleteDbs
- },
-
% set up cluster-stable listener
- couch_replicator_clustering:link_cluster_event_listener(?MODULE,
- notify_cluster_event, [self()]),
+ Period = abs(config:get_integer("couch_peruser", "cluster_quiet_period",
+ ?DEFAULT_QUIET_PERIOD)),
+ StartPeriod = abs(config:get_integer("couch_peruser", "cluster_start_period",
+ ?DEFAULT_START_PERIOD)),
+
+ {ok, Mem3Cluster} = mem3_cluster:start_link(?MODULE, self(), StartPeriod,
+ Period),
couch_log:debug("peruser: registered for cluster event on node ~p", [node()]),
- ClusterState
+
+ #clusterState{
+ parent = self(),
+ db_name = DbName,
+ delete_dbs = DeleteDbs,
+ mem3_cluster_pid = Mem3Cluster,
+ cluster_stable = false
+ }
end.
% Cluster membership change notification callback
@@ -80,7 +100,7 @@ start_listening(#clusterState{db_name=DbName, delete_dbs=DeleteDbs} = ClusterSta
S#state{changes_pid=Pid, changes_ref=Ref}
end, mem3:local_shards(DbName)),
- ClusterState#clusterState{states = States}
+ ClusterState#clusterState{states = States, cluster_stable = true}
catch error:database_does_not_exist ->
couch_log:warning("couch_peruser can't proceed as underlying database (~s) is missing, disables itself.", [DbName]),
config:set("couch_peruser", "enable", "false", lists:concat([binary_to_list(DbName), " is missing"]))
@@ -166,6 +186,7 @@ ensure_user_db(User) ->
{ok, _DbInfo} = fabric:get_db_info(UserDb)
catch error:database_does_not_exist ->
case fabric:create_db(UserDb, [?ADMIN_CTX]) of
+ {error, file_exists} -> ok;
ok -> ok;
accepted -> ok
end
@@ -207,7 +228,7 @@ remove_user(User, Prop, {Modified, SecProps}) ->
ensure_security(User, UserDb, TransformFun) ->
case fabric:get_all_security(UserDb, [?ADMIN_CTX]) of
{error, no_majority} ->
- % single node, ignore
+ % single node, ignore
ok;
{ok, Shards} ->
{_ShardInfo, {SecProps}} = hd(Shards),
@@ -237,6 +258,16 @@ exit_changes(ClusterState) ->
exit(State#state.changes_pid, kill)
end, ClusterState#clusterState.states).
+% Mem3 cluster callbacks
+
+cluster_unstable(Server) ->
+ gen_server:cast(Server, cluster_unstable),
+ Server.
+
+cluster_stable(Server) ->
+ gen_server:cast(Server, cluster_stable),
+ Server.
+
%% gen_server callbacks
init([]) ->
@@ -254,12 +285,12 @@ handle_cast(update_config, _) ->
{noreply, init()};
handle_cast(stop, State) ->
{stop, normal, State};
-handle_cast({cluster, unstable}, ClusterState) when ClusterState#clusterState.states =/= undefined ->
+handle_cast(cluster_unstable, ClusterState) when ClusterState#clusterState.states =/= undefined ->
exit_changes(ClusterState),
{noreply, init()};
-handle_cast({cluster, unstable}, _) ->
+handle_cast(cluster_unstable, _) ->
{noreply, init()};
-handle_cast({cluster, stable}, State) ->
+handle_cast(cluster_stable, State) ->
{noreply, start_listening(State)};
handle_cast(_Msg, State) ->
{noreply, State}.
--
To stop receiving notification emails like this one, please contact
"commits@couchdb.apache.org" <co...@couchdb.apache.org>.
[couchdb] 08/10: make sure peruser listeners are only initialised
once per node
Posted by ja...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
jan pushed a commit to branch 749-fix-couch_peruser-app-structure
in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit 736c26984907116eff8a5b0dac705b9cfee4a714
Author: Jan Lehnardt <ja...@apache.org>
AuthorDate: Sun Oct 8 15:36:31 2017 +0200
make sure peruser listeners are only initialised once per node
---
src/couch_peruser/src/couch_peruser.erl | 59 ++++++++++++++++++++-------------
1 file changed, 36 insertions(+), 23 deletions(-)
diff --git a/src/couch_peruser/src/couch_peruser.erl b/src/couch_peruser/src/couch_peruser.erl
index 791431c..e722b7e 100644
--- a/src/couch_peruser/src/couch_peruser.erl
+++ b/src/couch_peruser/src/couch_peruser.erl
@@ -32,7 +32,14 @@
cluster_unstable/1
]).
--record(state, {parent, db_name, delete_dbs, changes_pid, changes_ref}).
+-record(state, {
+ parent,
+ db_name,
+ delete_dbs,
+ changes_pid,
+ changes_ref
+}).
+
-record(clusterState, {
parent,
db_name,
@@ -47,6 +54,9 @@
-define(DEFAULT_QUIET_PERIOD, 60). % seconds
-define(DEFAULT_START_PERIOD, 5). % seconds
+%%
+%% Please leave in the commented-out couch_log:debug calls, thanks! — Jan
+%%
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
@@ -72,8 +82,6 @@ init() ->
{ok, Mem3Cluster} = mem3_cluster:start_link(?MODULE, self(), StartPeriod,
Period),
- couch_log:debug("peruser: registered for cluster event on node ~p", [node()]),
-
#clusterState{
parent = self(),
db_name = DbName,
@@ -86,11 +94,14 @@ init() ->
% Cluster membership change notification callback
-spec notify_cluster_event(pid(), {cluster, any()}) -> ok.
notify_cluster_event(Server, {cluster, _} = Event) ->
- couch_log:debug("peruser: received cluster event ~p on node ~p", [Event, node()]),
+ % couch_log:debug("peruser: received cluster event ~p on node ~p", [Event, node()]),
gen_server:cast(Server, Event).
+start_listening(#clusterState{states=States}=ClusterState) when length(States) > 0 ->
+ % couch_log:debug("peruser: start_listening() already run on node ~p in pid ~p", [node(), self()]),
+ ClusterState;
start_listening(#clusterState{db_name=DbName, delete_dbs=DeleteDbs} = ClusterState) ->
- couch_log:debug("peruser: start_listening() on node ~p", [node()]),
+ % couch_log:debug("peruser: start_listening() on node ~p", [node()]),
try
States = lists:map(fun (A) ->
S = #state{parent = ClusterState#clusterState.parent,
@@ -100,6 +111,7 @@ start_listening(#clusterState{db_name=DbName, delete_dbs=DeleteDbs} = ClusterSta
?MODULE, init_changes_handler, [S], [link, monitor]),
S#state{changes_pid=Pid, changes_ref=Ref}
end, mem3:local_shards(DbName)),
+ % couch_log:debug("peruser: start_listening() States ~p", [States]),
ClusterState#clusterState{states = States, cluster_stable = true}
catch error:database_does_not_exist ->
@@ -108,7 +120,6 @@ start_listening(#clusterState{db_name=DbName, delete_dbs=DeleteDbs} = ClusterSta
end.
init_changes_handler(#state{db_name=DbName} = State) ->
- % leave for debugging
% couch_log:debug("peruser: init_changes_handler() on DbName ~p", [DbName]),
try
{ok, Db} = couch_db:open_int(DbName, [?ADMIN_CTX, sys_db]),
@@ -123,7 +134,6 @@ init_changes_handler(#state{db_name=DbName} = State) ->
changes_handler({change, {Doc}, _Prepend}, _ResType, State=#state{db_name=DbName}) ->
- % leave for debugging
% couch_log:debug("peruser: changes_handler() on DbName/Doc ~p/~p", [DbName, Doc]),
case couch_util:get_value(<<"id">>, Doc) of
@@ -155,28 +165,31 @@ changes_handler({change, {Doc}, _Prepend}, _ResType, State=#state{db_name=DbName
changes_handler(_Event, _ResType, State) ->
State.
+
should_handle_doc(ShardName, DocId) ->
- should_handle_doc_int(ShardName, DocId, is_stable()).
-
-should_handle_doc_int(ShardName, DocId, false) ->
- % when the cluster is unstable, we have already stopped all Listeners
- % the next stable event will restart all listeners and pick up this
- % doc change
- couch_log:debug("peruser: skipping, cluster unstable ~s/~s", [ShardName, DocId]),
- false;
-should_handle_doc_int(ShardName, DocId, true) ->
+ case is_stable() of
+ false ->
+ % when the cluster is unstable, we have already stopped all Listeners
+ % the next stable event will restart all listeners and pick up this
+ % doc change
+ couch_log:debug("peruser: skipping, cluster unstable ~s/~s", [ShardName, DocId]),
+ false;
+ true ->
+ should_handle_doc_int(ShardName, DocId)
+ end.
+
+should_handle_doc_int(ShardName, DocId) ->
DbName = mem3:dbname(ShardName),
Live = [erlang:node() | erlang:nodes()],
Shards = mem3:shards(DbName, DocId),
Nodes = [N || #shard{node=N} <- Shards, lists:member(N, Live)],
case mem3:owner(DbName, DocId, Nodes) of
- ThisNode when ThisNode =:= node() ->
- couch_log:debug("peruser: handling ~s/~s", [DbName, DocId]),
- % do the deed
- true;
- _OtherNode ->
- couch_log:debug("peruser: skipping ~s/~s", [DbName, DocId]),
- false
+ ThisNode when ThisNode =:= node() ->
+ couch_log:debug("peruser: handling ~s/~s", [DbName, DocId]),
+ true; % do the database action
+ _OtherNode ->
+ couch_log:debug("peruser: skipping ~s/~s", [DbName, DocId]),
+ false
end.
--
To stop receiving notification emails like this one, please contact
"commits@couchdb.apache.org" <co...@couchdb.apache.org>.
[couchdb] 03/10: feat: mango test runner: do not rely on timeout
for CouchDB start alone
Posted by ja...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
jan pushed a commit to branch 749-fix-couch_peruser-app-structure
in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit 18f558cf320954177a92a6636405e433868e5c27
Author: Jan Lehnardt <ja...@apache.org>
AuthorDate: Sun Oct 1 13:14:43 2017 +0200
feat: mango test runner: do not rely on timeout for CouchDB start alone
On slow build nodes, 10 seconds might not be enough of a wait.
---
test/build/test-run-couch-for-mango.sh | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/test/build/test-run-couch-for-mango.sh b/test/build/test-run-couch-for-mango.sh
index 6034a79..0597a8f 100755
--- a/test/build/test-run-couch-for-mango.sh
+++ b/test/build/test-run-couch-for-mango.sh
@@ -13,8 +13,17 @@
./dev/run -n 1 --admin=testuser:testpass &
export SERVER_PID=$!
-sleep 10
-curl http://dev:15984
+
+COUCH_STARTED=-1
+while ( [ $COUCH_STARTED -ne 0 ] ); do
+ curl -s http://127.0.0.1:15984
+ COUCH_STARTED=$?
+ if [ $COUCH_STARTED -ne 0 ]; then
+ # do not wait another 5 seconds if couch started now
+ sleep 5
+ fi
+done
+
cd src/mango/
nosetests
--
To stop receiving notification emails like this one, please contact
"commits@couchdb.apache.org" <co...@couchdb.apache.org>.
[couchdb] 09/10: add type specs
Posted by ja...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
jan pushed a commit to branch 749-fix-couch_peruser-app-structure
in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit 6203e08b322301d20d770265fdc0b11748b4289c
Author: Jan Lehnardt <ja...@apache.org>
AuthorDate: Sun Oct 8 16:28:38 2017 +0200
add type specs
---
src/couch_peruser/src/couch_peruser.erl | 53 +++++++++++++++++++++------------
1 file changed, 34 insertions(+), 19 deletions(-)
diff --git a/src/couch_peruser/src/couch_peruser.erl b/src/couch_peruser/src/couch_peruser.erl
index e722b7e..16305c6 100644
--- a/src/couch_peruser/src/couch_peruser.erl
+++ b/src/couch_peruser/src/couch_peruser.erl
@@ -33,20 +33,20 @@
]).
-record(state, {
- parent,
- db_name,
- delete_dbs,
- changes_pid,
- changes_ref
+ parent :: pid(),
+ db_name :: binary(),
+ delete_dbs :: boolean(),
+ changes_pid :: pid(),
+ changes_ref :: reference()
}).
-record(clusterState, {
- parent,
- db_name,
- delete_dbs,
- states,
- mem3_cluster_pid,
- cluster_stable
+ parent :: pid(),
+ db_name :: binary(),
+ delete_dbs :: boolean(),
+ states :: list(),
+ mem3_cluster_pid :: pid(),
+ cluster_stable :: boolean()
}).
-define(USERDB_PREFIX, "userdb-").
@@ -57,10 +57,11 @@
%%
%% Please leave in the commented-out couch_log:debug calls, thanks! — Jan
%%
-
+-spec start_link() -> {ok, pid()} | ignore | {error, term()}.
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
+-spec init() -> #clusterState{}.
init() ->
couch_log:debug("peruser: starting on node ~p in pid ~p", [node(), self()]),
case config:get_boolean("couch_peruser", "enable", false) of
@@ -92,11 +93,12 @@ init() ->
end.
% Cluster membership change notification callback
--spec notify_cluster_event(pid(), {cluster, any()}) -> ok.
+-spec notify_cluster_event(Server :: pid(), Event :: {cluster, any()}) -> ok.
notify_cluster_event(Server, {cluster, _} = Event) ->
% couch_log:debug("peruser: received cluster event ~p on node ~p", [Event, node()]),
gen_server:cast(Server, Event).
+-spec start_listening(ClusterState :: #clusterState{}) -> #clusterState{} | ok.
start_listening(#clusterState{states=States}=ClusterState) when length(States) > 0 ->
% couch_log:debug("peruser: start_listening() already run on node ~p in pid ~p", [node(), self()]),
ClusterState;
@@ -119,6 +121,7 @@ start_listening(#clusterState{db_name=DbName, delete_dbs=DeleteDbs} = ClusterSta
config:set("couch_peruser", "enable", "false", lists:concat([binary_to_list(DbName), " is missing"]))
end.
+-spec init_changes_handler(State :: #state{}) -> ok.
init_changes_handler(#state{db_name=DbName} = State) ->
% couch_log:debug("peruser: init_changes_handler() on DbName ~p", [DbName]),
try
@@ -132,7 +135,8 @@ init_changes_handler(#state{db_name=DbName} = State) ->
ok
end.
-
+-type db_change() :: {atom(), tuple(), binary()}.
+-spec changes_handler(Change :: db_change(), ResultType :: any(), State :: #state{}) -> #state{}.
changes_handler({change, {Doc}, _Prepend}, _ResType, State=#state{db_name=DbName}) ->
% couch_log:debug("peruser: changes_handler() on DbName/Doc ~p/~p", [DbName, Doc]),
@@ -165,7 +169,7 @@ changes_handler({change, {Doc}, _Prepend}, _ResType, State=#state{db_name=DbName
changes_handler(_Event, _ResType, State) ->
State.
-
+-spec should_handle_doc(ShardName :: binary(), DocId::binary()) -> boolean().
should_handle_doc(ShardName, DocId) ->
case is_stable() of
false ->
@@ -178,6 +182,7 @@ should_handle_doc(ShardName, DocId) ->
should_handle_doc_int(ShardName, DocId)
end.
+-spec should_handle_doc_int(ShardName :: binary(), DocId :: binary()) -> boolean().
should_handle_doc_int(ShardName, DocId) ->
DbName = mem3:dbname(ShardName),
Live = [erlang:node() | erlang:nodes()],
@@ -192,7 +197,7 @@ should_handle_doc_int(ShardName, DocId) ->
false
end.
-
+-spec delete_user_db(User :: binary()) -> binary().
delete_user_db(User) ->
UserDb = user_db_name(User),
try
@@ -205,6 +210,7 @@ delete_user_db(User) ->
end,
UserDb.
+-spec ensure_user_db(User :: binary()) -> binary().
ensure_user_db(User) ->
UserDb = user_db_name(User),
try
@@ -218,6 +224,7 @@ ensure_user_db(User) ->
end,
UserDb.
+-spec add_user(User :: binary(), Properties :: tuple(), Acc :: tuple()) -> tuple().
add_user(User, Prop, {Modified, SecProps}) ->
{PropValue} = couch_util:get_value(Prop, SecProps, {[]}),
Names = couch_util:get_value(<<"names">>, PropValue, []),
@@ -234,6 +241,7 @@ add_user(User, Prop, {Modified, SecProps}) ->
{<<"names">>, [User | Names]})}})}
end.
+-spec remove_user(User :: binary(), Properties :: tuple(), Acc :: tuple()) -> tuple().
remove_user(User, Prop, {Modified, SecProps}) ->
{PropValue} = couch_util:get_value(Prop, SecProps, {[]}),
Names = couch_util:get_value(<<"names">>, PropValue, []),
@@ -250,10 +258,11 @@ remove_user(User, Prop, {Modified, SecProps}) ->
{<<"names">>, lists:delete(User, Names)})}})}
end.
+-spec ensure_security(User :: binary(), UserDb :: binary(), TransformFun :: fun()) -> ok.
ensure_security(User, UserDb, TransformFun) ->
case fabric:get_all_security(UserDb, [?ADMIN_CTX]) of
{error, no_majority} ->
- % single node, ignore
+ % TODO: make sure this is still true: single node, ignore
ok;
{ok, Shards} ->
{_ShardInfo, {SecProps}} = hd(Shards),
@@ -272,11 +281,13 @@ ensure_security(User, UserDb, TransformFun) ->
end
end.
+-spec user_db_name(User :: binary()) -> binary().
user_db_name(User) ->
HexUser = list_to_binary(
[string:to_lower(integer_to_list(X, 16)) || <<X>> <= User]),
<<?USERDB_PREFIX,HexUser/binary>>.
+-spec exit_changes(ClusterState :: #clusterState{}) -> ok.
exit_changes(ClusterState) ->
lists:foreach(fun (State) ->
demonitor(State#state.changes_ref, [flush]),
@@ -289,16 +300,20 @@ is_stable() ->
% Mem3 cluster callbacks
+% TODO: find out what type Server is
+-spec cluster_unstable(Server :: any()) -> any().
cluster_unstable(Server) ->
gen_server:cast(Server, cluster_unstable),
Server.
+% TODO: find out what type Server is
+-spec cluster_stable(Server :: any()) -> any().
cluster_stable(Server) ->
gen_server:cast(Server, cluster_stable),
Server.
%% gen_server callbacks
-
+-spec init(Options :: list()) -> {ok, #clusterState{}}.
init([]) ->
ok = subscribe_for_changes(),
{ok, init()}.
@@ -344,13 +359,13 @@ handle_info(restart_config_listener, State) ->
handle_info(_Msg, State) ->
{noreply, State}.
+-spec subscribe_for_changes() -> ok.
subscribe_for_changes() ->
config:subscribe_for_changes([
{"couch_httpd_auth", "authentication_db"},
"couch_peruser"
]).
-
terminate(_Reason, _State) ->
%% Everything should be linked or monitored, let nature
%% take its course.
--
To stop receiving notification emails like this one, please contact
"commits@couchdb.apache.org" <co...@couchdb.apache.org>.