You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by da...@apache.org on 2010/06/23 21:11:51 UTC
svn commit: r957314 - in /couchdb/trunk: etc/couchdb/ share/
share/www/script/ share/www/script/test/ src/couchdb/ src/couchdb/priv/
Author: damien
Date: Wed Jun 23 19:11:50 2010
New Revision: 957314
URL: http://svn.apache.org/viewvc?rev=957314&view=rev
Log:
Authentication caching, to avoid repeated opening and closing of the users database for each request requiring authentication. COUCHDB-807
Modified:
couchdb/trunk/etc/couchdb/default.ini.tpl.in
couchdb/trunk/share/Makefile.am
couchdb/trunk/share/www/script/couch_tests.js
couchdb/trunk/share/www/script/test/users_db.js
couchdb/trunk/src/couchdb/Makefile.am
couchdb/trunk/src/couchdb/couch_db.erl
couchdb/trunk/src/couchdb/couch_db.hrl
couchdb/trunk/src/couchdb/couch_db_updater.erl
couchdb/trunk/src/couchdb/couch_file.erl
couchdb/trunk/src/couchdb/couch_httpd_auth.erl
couchdb/trunk/src/couchdb/couch_httpd_db.erl
couchdb/trunk/src/couchdb/couch_httpd_oauth.erl
couchdb/trunk/src/couchdb/couch_server.erl
couchdb/trunk/src/couchdb/priv/stat_descriptions.cfg.in
Modified: couchdb/trunk/etc/couchdb/default.ini.tpl.in
URL: http://svn.apache.org/viewvc/couchdb/trunk/etc/couchdb/default.ini.tpl.in?rev=957314&r1=957313&r2=957314&view=diff
==============================================================================
--- couchdb/trunk/etc/couchdb/default.ini.tpl.in (original)
+++ couchdb/trunk/etc/couchdb/default.ini.tpl.in Wed Jun 23 19:11:50 2010
@@ -31,6 +31,7 @@ include_sasl = true
authentication_db = _users
require_valid_user = false
timeout = 600 ; number of seconds before automatic logout
+auth_cache_size = 50 ; size is number of cache entries
[query_servers]
javascript = %bindir%/%couchjs_command_name% %localbuilddatadir%/server/main.js
@@ -55,6 +56,7 @@ httpd={couch_httpd, start_link, []}
stats_aggregator={couch_stats_aggregator, start, []}
stats_collector={couch_stats_collector, start, []}
uuids={couch_uuids, start, []}
+auth_cache={couch_auth_cache, start_link, []}
[httpd_global_handlers]
/ = {couch_httpd_misc_handlers, handle_welcome_req, <<"Welcome">>}
Modified: couchdb/trunk/share/Makefile.am
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/Makefile.am?rev=957314&r1=957313&r2=957314&view=diff
==============================================================================
--- couchdb/trunk/share/Makefile.am (original)
+++ couchdb/trunk/share/Makefile.am Wed Jun 23 19:11:50 2010
@@ -110,6 +110,7 @@ nobase_dist_localdata_DATA = \
www/script/test/attachment_names.js \
www/script/test/attachment_paths.js \
www/script/test/attachment_views.js \
+ www/script/test/auth_cache.js \
www/script/test/basics.js \
www/script/test/batch_save.js \
www/script/test/bulk_docs.js \
Modified: couchdb/trunk/share/www/script/couch_tests.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/couch_tests.js?rev=957314&r1=957313&r2=957314&view=diff
==============================================================================
--- couchdb/trunk/share/www/script/couch_tests.js [utf-8] (original)
+++ couchdb/trunk/share/www/script/couch_tests.js [utf-8] Wed Jun 23 19:11:50 2010
@@ -35,6 +35,7 @@ loadTest("attachments_multipart.js");
loadTest("attachment_names.js");
loadTest("attachment_paths.js");
loadTest("attachment_views.js");
+loadTest("auth_cache.js");
loadTest("batch_save.js");
loadTest("bulk_docs.js");
loadTest("changes.js");
Modified: couchdb/trunk/share/www/script/test/users_db.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/users_db.js?rev=957314&r1=957313&r2=957314&view=diff
==============================================================================
--- couchdb/trunk/share/www/script/test/users_db.js (original)
+++ couchdb/trunk/share/www/script/test/users_db.js Wed Jun 23 19:11:50 2010
@@ -24,8 +24,6 @@ couchTests.users_db = function(debug) {
// to determine the actual users db name.
function testFun() {
- usersDb.deleteDb();
-
// test that the validation function is installed
var ddoc = usersDb.open("_design/_auth");
T(ddoc.validate_doc_update);
@@ -89,10 +87,12 @@ couchTests.users_db = function(debug) {
};
+ usersDb.deleteDb();
run_on_modified_server(
[{section: "couch_httpd_auth",
- key: "authentication_db", value: "test_suite_users"}],
+ key: "authentication_db", value: usersDb.name}],
testFun
);
+ usersDb.deleteDb(); // cleanup
}
\ No newline at end of file
Modified: couchdb/trunk/src/couchdb/Makefile.am
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/Makefile.am?rev=957314&r1=957313&r2=957314&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/Makefile.am (original)
+++ couchdb/trunk/src/couchdb/Makefile.am Wed Jun 23 19:11:50 2010
@@ -17,7 +17,7 @@ couchlibdir = $(localerlanglibdir)/couch
couchincludedir = $(couchlibdir)/include
couchebindir = $(couchlibdir)/ebin
-couchinclude_DATA = couch_db.hrl
+couchinclude_DATA = couch_db.hrl couch_js_functions.hrl
couchebin_DATA = $(compiled_files)
# dist_devdoc_DATA = $(doc_base) $(doc_modules)
@@ -29,6 +29,7 @@ CLEANFILES = $(compiled_files) $(doc_bas
source_files = \
couch.erl \
couch_app.erl \
+ couch_auth_cache.erl \
couch_btree.erl \
couch_changes.erl \
couch_config.erl \
@@ -80,12 +81,13 @@ source_files = \
couch_db_updater.erl \
couch_work_queue.erl
-EXTRA_DIST = $(source_files) couch_db.hrl
+EXTRA_DIST = $(source_files) couch_db.hrl couch_js_functions.hrl
compiled_files = \
couch.app \
couch.beam \
couch_app.beam \
+ couch_auth_cache.beam \
couch_btree.beam \
couch_changes.beam \
couch_config.beam \
@@ -194,6 +196,6 @@ endif
# $(ERL) -noshell -run edoc_run files [\"$<\"]
-%.beam: %.erl couch_db.hrl
+%.beam: %.erl couch_db.hrl couch_js_functions.hrl
$(ERLC) $(ERLC_FLAGS) ${TEST} $<;
Modified: couchdb/trunk/src/couchdb/couch_db.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_db.erl?rev=957314&r1=957313&r2=957314&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_db.erl (original)
+++ couchdb/trunk/src/couchdb/couch_db.erl Wed Jun 23 19:11:50 2010
@@ -114,7 +114,7 @@ open_doc(Db, IdOrDocInfo) ->
open_doc(Db, IdOrDocInfo, []).
open_doc(Db, Id, Options) ->
- couch_stats_collector:increment({couchdb, database_reads}),
+ increment_stat(Db, {couchdb, database_reads}),
case open_doc_int(Db, Id, Options) of
{ok, #doc{deleted=true}=Doc} ->
case lists:member(deleted, Options) of
@@ -157,7 +157,7 @@ find_ancestor_rev_pos({RevPos, [RevId|Re
end.
open_doc_revs(Db, Id, Revs, Options) ->
- couch_stats_collector:increment({couchdb, database_reads}),
+ increment_stat(Db, {couchdb, database_reads}),
[{ok, Results}] = open_doc_revs_int(Db, [{Id, Revs}], Options),
{ok, [apply_open_options(Result, Options) || Result <- Results]}.
@@ -621,7 +621,7 @@ check_dup_atts2(_) ->
update_docs(Db, Docs, Options, replicated_changes) ->
- couch_stats_collector:increment({couchdb, database_writes}),
+ increment_stat(Db, {couchdb, database_writes}),
DocBuckets = group_alike_docs(Docs),
case (Db#db.validate_doc_funs /= []) orelse
@@ -647,7 +647,7 @@ update_docs(Db, Docs, Options, replicate
{ok, DocErrors};
update_docs(Db, Docs, Options, interactive_edit) ->
- couch_stats_collector:increment({couchdb, database_writes}),
+ increment_stat(Db, {couchdb, database_writes}),
AllOrNothing = lists:member(all_or_nothing, Options),
% go ahead and generate the new revision ids for the documents.
% separate out the NonRep documents from the rest of the documents
@@ -959,7 +959,12 @@ init({DbName, Filepath, Fd, Options}) ->
{ok, UpdaterPid} = gen_server:start_link(couch_db_updater, {self(), DbName, Filepath, Fd, Options}, []),
{ok, #db{fd_ref_counter=RefCntr}=Db} = gen_server:call(UpdaterPid, get_db),
couch_ref_counter:add(RefCntr),
- couch_stats_collector:track_process_count({couchdb, open_databases}),
+ case lists:member(sys_db, Options) of
+ true ->
+ ok;
+ false ->
+ couch_stats_collector:track_process_count({couchdb, open_databases})
+ end,
process_flag(trap_exit, true),
{ok, Db}.
@@ -983,7 +988,9 @@ handle_call({db_updated, NewDb}, _From,
couch_ref_counter:add(NewRefCntr),
couch_ref_counter:drop(OldRefCntr)
end,
- {reply, ok, NewDb}.
+ {reply, ok, NewDb};
+handle_call(get_db, _From, Db) ->
+ {reply, {ok, Db}, Db}.
handle_cast(Msg, Db) ->
@@ -1178,4 +1185,7 @@ make_doc(#db{fd=Fd}=Db, Id, Deleted, Bp,
}.
-
+increment_stat(#db{is_sys_db = true}, _Stat) ->
+ ok;
+increment_stat(#db{}, Stat) ->
+ couch_stats_collector:increment(Stat).
Modified: couchdb/trunk/src/couchdb/couch_db.hrl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_db.hrl?rev=957314&r1=957313&r2=957314&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_db.hrl (original)
+++ couchdb/trunk/src/couchdb/couch_db.hrl Wed Jun 23 19:11:50 2010
@@ -173,7 +173,8 @@
user_ctx = #user_ctx{},
waiting_delayed_commit = nil,
revs_limit = 1000,
- fsync_options = []
+ fsync_options = [],
+ is_sys_db = false
}).
Modified: couchdb/trunk/src/couchdb/couch_db_updater.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_db_updater.erl?rev=957314&r1=957313&r2=957314&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_db_updater.erl (original)
+++ couchdb/trunk/src/couchdb/couch_db_updater.erl Wed Jun 23 19:11:50 2010
@@ -35,7 +35,7 @@ init({MainPid, DbName, Filepath, Fd, Opt
Db = init_db(DbName, Filepath, Fd, Header),
Db2 = refresh_validate_doc_funs(Db),
- {ok, Db2#db{main_pid=MainPid}}.
+ {ok, Db2#db{main_pid = MainPid, is_sys_db = lists:member(sys_db, Options)}}.
terminate(_Reason, Db) ->
Modified: couchdb/trunk/src/couchdb/couch_file.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_file.erl?rev=957314&r1=957313&r2=957314&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_file.erl (original)
+++ couchdb/trunk/src/couchdb/couch_file.erl Wed Jun 23 19:11:50 2010
@@ -231,16 +231,14 @@ init({Filepath, Options, ReturnPid, Ref}
{ok, 0} = file:position(Fd, 0),
ok = file:truncate(Fd),
ok = file:sync(Fd),
- couch_stats_collector:track_process_count(
- {couchdb, open_os_files}),
+ maybe_track_open_os_files(Options),
{ok, #file{fd=Fd}};
false ->
ok = file:close(Fd),
init_status_error(ReturnPid, Ref, file_exists)
end;
false ->
- couch_stats_collector:track_process_count(
- {couchdb, open_os_files}),
+ maybe_track_open_os_files(Options),
{ok, #file{fd=Fd}}
end;
Error ->
@@ -252,7 +250,7 @@ init({Filepath, Options, ReturnPid, Ref}
{ok, Fd_Read} ->
{ok, Fd} = file:open(Filepath, [read, append, raw, binary]),
ok = file:close(Fd_Read),
- couch_stats_collector:track_process_count({couchdb, open_os_files}),
+ maybe_track_open_os_files(Options),
{ok, Length} = file:position(Fd, eof),
{ok, #file{fd=Fd, eof=Length}};
Error ->
@@ -260,6 +258,13 @@ init({Filepath, Options, ReturnPid, Ref}
end
end.
+maybe_track_open_os_files(FileOptions) ->
+ case lists:member(sys_db, FileOptions) of
+ true ->
+ ok;
+ false ->
+ couch_stats_collector:track_process_count({couchdb, open_os_files})
+ end.
terminate(_Reason, _Fd) ->
ok.
Modified: couchdb/trunk/src/couchdb/couch_httpd_auth.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd_auth.erl?rev=957314&r1=957313&r2=957314&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_httpd_auth.erl (original)
+++ couchdb/trunk/src/couchdb/couch_httpd_auth.erl Wed Jun 23 19:11:50 2010
@@ -19,7 +19,6 @@
-export([proxy_authentification_handler/1]).
-export([cookie_auth_header/2]).
-export([handle_session_req/1]).
--export([ensure_users_db_exists/1, get_user/1]).
-import(couch_httpd, [header_value/2, send_json/2,send_json/4, send_method_not_allowed/2]).
@@ -66,7 +65,7 @@ basic_name_pw(Req) ->
default_authentication_handler(Req) ->
case basic_name_pw(Req) of
{User, Pass} ->
- case get_user(?l2b(User)) of
+ case couch_auth_cache:get_user_creds(User) of
nil ->
throw({unauthorized, <<"Name or password is incorrect.">>});
UserProps ->
@@ -156,149 +155,6 @@ proxy_auth_user(Req) ->
end
end.
-% maybe we can use hovercraft to simplify running this view query
-% rename to get_user_from_users_db
-get_user(UserName) ->
- case couch_config:get("admins", ?b2l(UserName)) of
- "-hashed-" ++ HashedPwdAndSalt ->
- % the name is an admin, now check to see if there is a user doc
- % which has a matching name, salt, and password_sha
- [HashedPwd, Salt] = string:tokens(HashedPwdAndSalt, ","),
- case get_user_props_from_db(UserName) of
- nil ->
- [{<<"roles">>, [<<"_admin">>]},
- {<<"salt">>, ?l2b(Salt)},
- {<<"password_sha">>, ?l2b(HashedPwd)}];
- UserProps when is_list(UserProps) ->
- DocRoles = couch_util:get_value(<<"roles">>, UserProps),
- [{<<"roles">>, [<<"_admin">> | DocRoles]},
- {<<"salt">>, ?l2b(Salt)},
- {<<"password_sha">>, ?l2b(HashedPwd)}]
- end;
- _Else ->
- get_user_props_from_db(UserName)
- end.
-
-get_user_props_from_db(UserName) ->
- DbName = couch_config:get("couch_httpd_auth", "authentication_db"),
- {ok, Db} = ensure_users_db_exists(?l2b(DbName)),
- DocId = <<"org.couchdb.user:", UserName/binary>>,
- try couch_httpd_db:couch_doc_open(Db, DocId, nil, [conflicts]) of
- #doc{meta=Meta}=Doc ->
- % check here for conflict state and throw error if conflicted
- case couch_util:get_value(conflicts,Meta,[]) of
- [] ->
- {DocProps} = couch_query_servers:json_doc(Doc),
- case couch_util:get_value(<<"type">>, DocProps) of
- <<"user">> ->
- DocProps;
- _Else ->
- ?LOG_ERROR("Invalid user doc. Id: ~p",[DocId]),
- nil
- end;
- _Else ->
- throw({unauthorized, <<"User document conflict must be resolved before login.">>})
- end
- catch
- throw:_Throw ->
- nil
- after
- couch_db:close(Db)
- end.
-
-% this should handle creating the ddoc
-ensure_users_db_exists(DbName) ->
- case couch_db:open(DbName, [{user_ctx, #user_ctx{roles=[<<"_admin">>]}}]) of
- {ok, Db} ->
- ensure_auth_ddoc_exists(Db, <<"_design/_auth">>),
- {ok, Db};
- _Error ->
- {ok, Db} = couch_db:create(DbName, [{user_ctx, #user_ctx{roles=[<<"_admin">>]}}]),
- ensure_auth_ddoc_exists(Db, <<"_design/_auth">>),
- {ok, Db}
- end.
-
-ensure_auth_ddoc_exists(Db, DDocId) ->
- try couch_httpd_db:couch_doc_open(Db, DDocId, nil, []) of
- _Foo -> ok
- catch
- _:_Error ->
- % create the design document
- {ok, AuthDesign} = auth_design_doc(DDocId),
- {ok, _Rev} = couch_db:update_doc(Db, AuthDesign, []),
- ok
- end.
-
-% add the validation function here
-auth_design_doc(DocId) ->
- DocProps = [
- {<<"_id">>, DocId},
- {<<"language">>,<<"javascript">>},
- {
- <<"validate_doc_update">>,
- <<"function(newDoc, oldDoc, userCtx) {
- if ((oldDoc || newDoc).type != 'user') {
- throw({forbidden : 'doc.type must be user'});
- } // we only validate user docs for now
- if (newDoc._deleted === true) {
- // allow deletes by admins and matching users
- // without checking the other fields
- if ((userCtx.roles.indexOf('_admin') != -1) || (userCtx.name == oldDoc.name)) {
- return;
- } else {
- throw({forbidden : 'Only admins may delete other user docs.'});
- }
- }
- if (!newDoc.name) {
- throw({forbidden : 'doc.name is required'});
- }
- if (!(newDoc.roles && (typeof newDoc.roles.length != 'undefined') )) {
- throw({forbidden : 'doc.roles must be an array'});
- }
- if (newDoc._id != 'org.couchdb.user:'+newDoc.name) {
- throw({forbidden : 'Docid must be of the form org.couchdb.user:name'});
- }
- if (oldDoc) { // validate all updates
- if (oldDoc.name != newDoc.name) {
- throw({forbidden : 'Usernames may not be changed.'});
- }
- }
- if (newDoc.password_sha && !newDoc.salt) {
- throw({forbidden : 'Users with password_sha must have a salt. See /_utils/script/couch.js for example code.'});
- }
- if (userCtx.roles.indexOf('_admin') == -1) { // not an admin
- if (oldDoc) { // validate non-admin updates
- if (userCtx.name != newDoc.name) {
- throw({forbidden : 'You may only update your own user document.'});
- }
- // validate role updates
- var oldRoles = oldDoc.roles.sort();
- var newRoles = newDoc.roles.sort();
- if (oldRoles.length != newRoles.length) {
- throw({forbidden : 'Only _admin may edit roles'});
- }
- for (var i=0; i < oldRoles.length; i++) {
- if (oldRoles[i] != newRoles[i]) {
- throw({forbidden : 'Only _admin may edit roles'});
- }
- };
- } else if (newDoc.roles.length > 0) {
- throw({forbidden : 'Only _admin may set roles'});
- }
- }
- // no system roles in users db
- for (var i=0; i < newDoc.roles.length; i++) {
- if (newDoc.roles[i][0] == '_') {
- throw({forbidden : 'No system roles (starting with underscore) in users db.'});
- }
- };
- // no system names as names
- if (newDoc.name[0] == '_') {
- throw({forbidden : 'Username may not start with underscore.'});
- }
- }">>
- }],
- {ok, couch_doc:from_json_obj({DocProps})}.
cookie_authentication_handler(#httpd{mochi_req=MochiReq}=Req) ->
case MochiReq:get_cookie_value("AuthSession") of
@@ -321,7 +177,7 @@ cookie_authentication_handler(#httpd{moc
Req;
SecretStr ->
Secret = ?l2b(SecretStr),
- case get_user(?l2b(User)) of
+ case couch_auth_cache:get_user_creds(User) of
nil -> Req;
UserProps ->
UserSalt = couch_util:get_value(<<"salt">>, UserProps, <<"">>),
@@ -403,7 +259,7 @@ handle_session_req(#httpd{method='POST',
UserName = ?l2b(couch_util:get_value("name", Form, "")),
Password = ?l2b(couch_util:get_value("password", Form, "")),
?LOG_DEBUG("Attempt Login: ~s",[UserName]),
- User = case get_user(UserName) of
+ User = case couch_auth_cache:get_user_creds(UserName) of
nil -> [];
Result -> Result
end,
Modified: couchdb/trunk/src/couchdb/couch_httpd_db.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd_db.erl?rev=957314&r1=957313&r2=957314&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_httpd_db.erl (original)
+++ couchdb/trunk/src/couchdb/couch_httpd_db.erl Wed Jun 23 19:11:50 2010
@@ -160,23 +160,13 @@ handle_design_info_req(Req, _Db, _DDoc)
create_db_req(#httpd{user_ctx=UserCtx}=Req, DbName) ->
ok = couch_httpd:verify_is_server_admin(Req),
- LDbName = ?b2l(DbName),
- case couch_config:get("couch_httpd_auth", "authentication_db") of
- LDbName ->
- % make sure user's db always has the auth ddoc
- {ok, Db} = couch_httpd_auth:ensure_users_db_exists(DbName),
- couch_db:close(Db),
- DbUrl = absolute_uri(Req, "/" ++ couch_util:url_encode(DbName)),
- send_json(Req, 201, [{"Location", DbUrl}], {[{ok, true}]});
- _Else ->
- case couch_server:create(DbName, [{user_ctx, UserCtx}]) of
- {ok, Db} ->
- couch_db:close(Db),
- DbUrl = absolute_uri(Req, "/" ++ couch_util:url_encode(DbName)),
- send_json(Req, 201, [{"Location", DbUrl}], {[{ok, true}]});
- Error ->
- throw(Error)
- end
+ case couch_server:create(DbName, [{user_ctx, UserCtx}]) of
+ {ok, Db} ->
+ couch_db:close(Db),
+ DbUrl = absolute_uri(Req, "/" ++ couch_util:url_encode(DbName)),
+ send_json(Req, 201, [{"Location", DbUrl}], {[{ok, true}]});
+ Error ->
+ throw(Error)
end.
delete_db_req(#httpd{user_ctx=UserCtx}=Req, DbName) ->
@@ -189,15 +179,6 @@ delete_db_req(#httpd{user_ctx=UserCtx}=R
end.
do_db_req(#httpd{user_ctx=UserCtx,path_parts=[DbName|_]}=Req, Fun) ->
- LDbName = ?b2l(DbName),
- % I hope this lookup is cheap.
- case couch_config:get("couch_httpd_auth", "authentication_db") of
- LDbName ->
- % make sure user's db always has the auth ddoc
- {ok, ADb} = couch_httpd_auth:ensure_users_db_exists(DbName),
- couch_db:close(ADb);
- _Else -> ok
- end,
case couch_db:open(DbName, [{user_ctx, UserCtx}]) of
{ok, Db} ->
try
Modified: couchdb/trunk/src/couchdb/couch_httpd_oauth.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd_oauth.erl?rev=957314&r1=957313&r2=957314&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_httpd_oauth.erl (original)
+++ couchdb/trunk/src/couchdb/couch_httpd_oauth.erl Wed Jun 23 19:11:50 2010
@@ -41,7 +41,7 @@ set_user_ctx(Req, AccessToken) ->
undefined -> throw({bad_request, unknown_oauth_token});
Value -> ?l2b(Value)
end,
- case couch_httpd_auth:get_user(Name) of
+ case couch_auth_cache:get_user_creds(Name) of
nil -> Req;
User ->
Roles = couch_util:get_value(<<"roles">>, User, []),
Modified: couchdb/trunk/src/couchdb/couch_server.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_server.erl?rev=957314&r1=957313&r2=957314&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_server.erl (original)
+++ couchdb/trunk/src/couchdb/couch_server.erl Wed Jun 23 19:11:50 2010
@@ -141,6 +141,7 @@ init([]) ->
ets:new(couch_dbs_by_name, [set, private, named_table]),
ets:new(couch_dbs_by_pid, [set, private, named_table]),
ets:new(couch_dbs_by_lru, [ordered_set, private, named_table]),
+ ets:new(couch_sys_dbs, [set, private, named_table]),
process_flag(trap_exit, true),
{ok, #server{root_dir=RootDir,
dbname_regexp=RegExp,
@@ -180,7 +181,7 @@ maybe_close_lru_db(#server{dbs_open=NumO
end.
try_close_lru(StartTime) ->
- LruTime = ets:first(couch_dbs_by_lru),
+ LruTime = get_lru(),
if LruTime > StartTime ->
% this means we've looped through all our opened dbs and found them
% all in use.
@@ -194,6 +195,7 @@ try_close_lru(StartTime) ->
true = ets:delete(couch_dbs_by_lru, LruTime),
true = ets:delete(couch_dbs_by_name, DbName),
true = ets:delete(couch_dbs_by_pid, MainPid),
+ true = ets:delete(couch_sys_dbs, DbName),
ok;
false ->
% this still has referrers. Go ahead and give it a current lru time
@@ -207,11 +209,31 @@ try_close_lru(StartTime) ->
end
end.
+get_lru() ->
+ get_lru(ets:first(couch_dbs_by_lru)).
+
+get_lru(LruTime) ->
+ [{LruTime, DbName}] = ets:lookup(couch_dbs_by_lru, LruTime),
+ case ets:member(couch_sys_dbs, DbName) of
+ false ->
+ LruTime;
+ true ->
+ [{_, {opened, MainPid, _}}] = ets:lookup(couch_dbs_by_name, DbName),
+ case couch_db:is_idle(MainPid) of
+ true ->
+ LruTime;
+ false ->
+ get_lru(ets:next(couch_dbs_by_lru, LruTime))
+ end
+ end.
+
open_async(Server, From, DbName, Filepath, Options) ->
Parent = self(),
Opener = spawn_link(fun() ->
Res = couch_db:start_link(DbName, Filepath, Options),
- gen_server:call(Parent, {open_result, DbName, Res}, infinity),
+ gen_server:call(
+ Parent, {open_result, DbName, Res, Options}, infinity
+ ),
unlink(Parent),
case Res of
{ok, DbReader} ->
@@ -222,13 +244,20 @@ open_async(Server, From, DbName, Filepat
end),
true = ets:insert(couch_dbs_by_name, {DbName, {opening, Opener, [From]}}),
true = ets:insert(couch_dbs_by_pid, {Opener, DbName}),
- Server#server{dbs_open=Server#server.dbs_open + 1}.
+ DbsOpen = case lists:member(sys_db, Options) of
+ true ->
+ true = ets:insert(couch_sys_dbs, {DbName, true}),
+ Server#server.dbs_open;
+ false ->
+ Server#server.dbs_open + 1
+ end,
+ Server#server{dbs_open = DbsOpen}.
handle_call({set_max_dbs_open, Max}, _From, Server) ->
{reply, ok, Server#server{max_dbs_open=Max}};
handle_call(get_server, _From, Server) ->
{reply, {ok, Server}, Server};
-handle_call({open_result, DbName, {ok, OpenedDbPid}}, _From, Server) ->
+handle_call({open_result, DbName, {ok, OpenedDbPid}, Options}, _From, Server) ->
link(OpenedDbPid),
[{DbName, {opening,Opener,Froms}}] = ets:lookup(couch_dbs_by_name, DbName),
lists:foreach(fun({FromPid,_}=From) ->
@@ -241,15 +270,28 @@ handle_call({open_result, DbName, {ok, O
true = ets:delete(couch_dbs_by_pid, Opener),
true = ets:insert(couch_dbs_by_pid, {OpenedDbPid, DbName}),
true = ets:insert(couch_dbs_by_lru, {LruTime, DbName}),
+ case lists:member(create, Options) of
+ true ->
+ couch_db_update_notifier:notify({created, DbName});
+ false ->
+ ok
+ end,
{reply, ok, Server};
-handle_call({open_result, DbName, Error}, _From, Server) ->
+handle_call({open_result, DbName, Error, Options}, _From, Server) ->
[{DbName, {opening,Opener,Froms}}] = ets:lookup(couch_dbs_by_name, DbName),
lists:foreach(fun(From) ->
gen_server:reply(From, Error)
end, Froms),
true = ets:delete(couch_dbs_by_name, DbName),
true = ets:delete(couch_dbs_by_pid, Opener),
- {reply, ok, Server#server{dbs_open=Server#server.dbs_open - 1}};
+ DbsOpen = case lists:member(sys_db, Options) of
+ true ->
+ true = ets:delete(couch_sys_dbs, DbName),
+ Server#server.dbs_open;
+ false ->
+ Server#server.dbs_open - 1
+ end,
+ {reply, ok, Server#server{dbs_open = DbsOpen}};
handle_call({open, DbName, Options}, {FromPid,_}=From, Server) ->
LruTime = now(),
case ets:lookup(couch_dbs_by_name, DbName) of
@@ -257,12 +299,17 @@ handle_call({open, DbName, Options}, {Fr
DbNameList = binary_to_list(DbName),
case check_dbname(Server, DbNameList) of
ok ->
- case maybe_close_lru_db(Server) of
- {ok, Server2} ->
- Filepath = get_full_filename(Server, DbNameList),
- {noreply, open_async(Server2, From, DbName, Filepath, Options)};
- CloseError ->
- {reply, CloseError, Server}
+ Path = get_full_filename(Server, DbNameList),
+ case lists:member(sys_db, Options) of
+ true ->
+ {noreply, open_async(Server, From, DbName, Path, Options)};
+ false ->
+ case maybe_close_lru_db(Server) of
+ {ok, Server2} ->
+ {noreply, open_async(Server2, From, DbName, Path, Options)};
+ CloseError ->
+ {reply, CloseError, Server}
+ end
end;
Error ->
{reply, Error, Server}
@@ -301,21 +348,34 @@ handle_call({delete, DbName, _Options},
case check_dbname(Server, DbNameList) of
ok ->
FullFilepath = get_full_filename(Server, DbNameList),
- Server2 =
+ UpdateState =
case ets:lookup(couch_dbs_by_name, DbName) of
- [] -> Server;
+ [] -> false;
[{_, {opening, Pid, Froms}}] ->
couch_util:shutdown_sync(Pid),
true = ets:delete(couch_dbs_by_name, DbName),
true = ets:delete(couch_dbs_by_pid, Pid),
[gen_server:send_result(F, not_found) || F <- Froms],
- Server#server{dbs_open=Server#server.dbs_open - 1};
+ true;
[{_, {opened, Pid, LruTime}}] ->
couch_util:shutdown_sync(Pid),
true = ets:delete(couch_dbs_by_name, DbName),
true = ets:delete(couch_dbs_by_pid, Pid),
true = ets:delete(couch_dbs_by_lru, LruTime),
- Server#server{dbs_open=Server#server.dbs_open - 1}
+ true
+ end,
+ Server2 = case UpdateState of
+ true ->
+ DbsOpen = case ets:member(couch_sys_dbs, DbName) of
+ true ->
+ true = ets:delete(couch_sys_dbs, DbName),
+ Server#server.dbs_open;
+ false ->
+ Server#server.dbs_open - 1
+ end,
+ Server#server{dbs_open = DbsOpen};
+ false ->
+ Server
end,
%% Delete any leftover .compact files. If we don't do this a subsequent
Modified: couchdb/trunk/src/couchdb/priv/stat_descriptions.cfg.in
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/priv/stat_descriptions.cfg.in?rev=957314&r1=957313&r2=957314&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/priv/stat_descriptions.cfg.in (original)
+++ couchdb/trunk/src/couchdb/priv/stat_descriptions.cfg.in Wed Jun 23 19:11:50 2010
@@ -19,6 +19,8 @@
{couchdb, open_databases, "number of open databases"}.
{couchdb, open_os_files, "number of file descriptors CouchDB has open"}.
{couchdb, request_time, "length of a request inside CouchDB without MochiWeb"}.
+{couchdb, auth_cache_hits, "number of authentication cache hits"}.
+{couchdb, auth_cache_misses, "number of authentication cache misses"}.
{httpd, bulk_requests, "number of bulk requests"}.
{httpd, requests, "number of HTTP requests"}.
Re: svn commit: r957314 - in /couchdb/trunk: etc/couchdb/ share/
share/www/script/ share/www/script/test/ src/couchdb/ src/couchdb/priv/
Posted by Filipe David Manana <fd...@gmail.com>.
And, most importantly src/couchdb/couch_auth_cache.erl
On Wed, Jun 23, 2010 at 8:19 PM, Filipe David Manana <fd...@gmail.com>wrote:
> Damien,
>
> the new files share/www/script/test/auth_cache.js and
> src/couchdb/couch_js_functions.hrl are missing in the diff.
>
> cheers
>
>
> On Wed, Jun 23, 2010 at 8:11 PM, <da...@apache.org> wrote:
>
>> Author: damien
>> Date: Wed Jun 23 19:11:50 2010
>> New Revision: 957314
>>
>> URL: http://svn.apache.org/viewvc?rev=957314&view=rev
>> Log:
>> Authentication caching, to avoid repeated opening and closing of the users
>> database for each request requiring authentication. COUCHDB-807
>>
>> Modified:
>> couchdb/trunk/etc/couchdb/default.ini.tpl.in
>> couchdb/trunk/share/Makefile.am
>> couchdb/trunk/share/www/script/couch_tests.js
>> couchdb/trunk/share/www/script/test/users_db.js
>> couchdb/trunk/src/couchdb/Makefile.am
>> couchdb/trunk/src/couchdb/couch_db.erl
>> couchdb/trunk/src/couchdb/couch_db.hrl
>> couchdb/trunk/src/couchdb/couch_db_updater.erl
>> couchdb/trunk/src/couchdb/couch_file.erl
>> couchdb/trunk/src/couchdb/couch_httpd_auth.erl
>> couchdb/trunk/src/couchdb/couch_httpd_db.erl
>> couchdb/trunk/src/couchdb/couch_httpd_oauth.erl
>> couchdb/trunk/src/couchdb/couch_server.erl
>> couchdb/trunk/src/couchdb/priv/stat_descriptions.cfg.in
>>
>> Modified: couchdb/trunk/etc/couchdb/default.ini.tpl.in
>> URL:
>> http://svn.apache.org/viewvc/couchdb/trunk/etc/couchdb/default.ini.tpl.in?rev=957314&r1=957313&r2=957314&view=diff
>>
>> ==============================================================================
>> --- couchdb/trunk/etc/couchdb/default.ini.tpl.in (original)
>> +++ couchdb/trunk/etc/couchdb/default.ini.tpl.in Wed Jun 23 19:11:50 2010
>> @@ -31,6 +31,7 @@ include_sasl = true
>> authentication_db = _users
>> require_valid_user = false
>> timeout = 600 ; number of seconds before automatic logout
>> +auth_cache_size = 50 ; size is number of cache entries
>>
>> [query_servers]
>> javascript = %bindir%/%couchjs_command_name%
>> %localbuilddatadir%/server/main.js
>> @@ -55,6 +56,7 @@ httpd={couch_httpd, start_link, []}
>> stats_aggregator={couch_stats_aggregator, start, []}
>> stats_collector={couch_stats_collector, start, []}
>> uuids={couch_uuids, start, []}
>> +auth_cache={couch_auth_cache, start_link, []}
>>
>> [httpd_global_handlers]
>> / = {couch_httpd_misc_handlers, handle_welcome_req, <<"Welcome">>}
>>
>> Modified: couchdb/trunk/share/Makefile.am
>> URL:
>> http://svn.apache.org/viewvc/couchdb/trunk/share/Makefile.am?rev=957314&r1=957313&r2=957314&view=diff
>>
>> ==============================================================================
>> --- couchdb/trunk/share/Makefile.am (original)
>> +++ couchdb/trunk/share/Makefile.am Wed Jun 23 19:11:50 2010
>> @@ -110,6 +110,7 @@ nobase_dist_localdata_DATA = \
>> www/script/test/attachment_names.js \
>> www/script/test/attachment_paths.js \
>> www/script/test/attachment_views.js \
>> + www/script/test/auth_cache.js \
>> www/script/test/basics.js \
>> www/script/test/batch_save.js \
>> www/script/test/bulk_docs.js \
>>
>> Modified: couchdb/trunk/share/www/script/couch_tests.js
>> URL:
>> http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/couch_tests.js?rev=957314&r1=957313&r2=957314&view=diff
>>
>> ==============================================================================
>> --- couchdb/trunk/share/www/script/couch_tests.js [utf-8] (original)
>> +++ couchdb/trunk/share/www/script/couch_tests.js [utf-8] Wed Jun 23
>> 19:11:50 2010
>> @@ -35,6 +35,7 @@ loadTest("attachments_multipart.js");
>> loadTest("attachment_names.js");
>> loadTest("attachment_paths.js");
>> loadTest("attachment_views.js");
>> +loadTest("auth_cache.js");
>> loadTest("batch_save.js");
>> loadTest("bulk_docs.js");
>> loadTest("changes.js");
>>
>> Modified: couchdb/trunk/share/www/script/test/users_db.js
>> URL:
>> http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/users_db.js?rev=957314&r1=957313&r2=957314&view=diff
>>
>> ==============================================================================
>> --- couchdb/trunk/share/www/script/test/users_db.js (original)
>> +++ couchdb/trunk/share/www/script/test/users_db.js Wed Jun 23 19:11:50
>> 2010
>> @@ -24,8 +24,6 @@ couchTests.users_db = function(debug) {
>> // to determine the actual users db name.
>>
>> function testFun() {
>> - usersDb.deleteDb();
>> -
>> // test that the validation function is installed
>> var ddoc = usersDb.open("_design/_auth");
>> T(ddoc.validate_doc_update);
>> @@ -89,10 +87,12 @@ couchTests.users_db = function(debug) {
>>
>> };
>>
>> + usersDb.deleteDb();
>> run_on_modified_server(
>> [{section: "couch_httpd_auth",
>> - key: "authentication_db", value: "test_suite_users"}],
>> + key: "authentication_db", value: usersDb.name}],
>> testFun
>> );
>> + usersDb.deleteDb(); // cleanup
>>
>> }
>> \ No newline at end of file
>>
>> Modified: couchdb/trunk/src/couchdb/Makefile.am
>> URL:
>> http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/Makefile.am?rev=957314&r1=957313&r2=957314&view=diff
>>
>> ==============================================================================
>> --- couchdb/trunk/src/couchdb/Makefile.am (original)
>> +++ couchdb/trunk/src/couchdb/Makefile.am Wed Jun 23 19:11:50 2010
>> @@ -17,7 +17,7 @@ couchlibdir = $(localerlanglibdir)/couch
>> couchincludedir = $(couchlibdir)/include
>> couchebindir = $(couchlibdir)/ebin
>>
>> -couchinclude_DATA = couch_db.hrl
>> +couchinclude_DATA = couch_db.hrl couch_js_functions.hrl
>> couchebin_DATA = $(compiled_files)
>>
>> # dist_devdoc_DATA = $(doc_base) $(doc_modules)
>> @@ -29,6 +29,7 @@ CLEANFILES = $(compiled_files) $(doc_bas
>> source_files = \
>> couch.erl \
>> couch_app.erl \
>> + couch_auth_cache.erl \
>> couch_btree.erl \
>> couch_changes.erl \
>> couch_config.erl \
>> @@ -80,12 +81,13 @@ source_files = \
>> couch_db_updater.erl \
>> couch_work_queue.erl
>>
>> -EXTRA_DIST = $(source_files) couch_db.hrl
>> +EXTRA_DIST = $(source_files) couch_db.hrl couch_js_functions.hrl
>>
>> compiled_files = \
>> couch.app \
>> couch.beam \
>> couch_app.beam \
>> + couch_auth_cache.beam \
>> couch_btree.beam \
>> couch_changes.beam \
>> couch_config.beam \
>> @@ -194,6 +196,6 @@ endif
>>
>> # $(ERL) -noshell -run edoc_run files [\"$<\"]
>>
>> -%.beam: %.erl couch_db.hrl
>> +%.beam: %.erl couch_db.hrl couch_js_functions.hrl
>> $(ERLC) $(ERLC_FLAGS) ${TEST} $<;
>>
>>
>> Modified: couchdb/trunk/src/couchdb/couch_db.erl
>> URL:
>> http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_db.erl?rev=957314&r1=957313&r2=957314&view=diff
>>
>> ==============================================================================
>> --- couchdb/trunk/src/couchdb/couch_db.erl (original)
>> +++ couchdb/trunk/src/couchdb/couch_db.erl Wed Jun 23 19:11:50 2010
>> @@ -114,7 +114,7 @@ open_doc(Db, IdOrDocInfo) ->
>> open_doc(Db, IdOrDocInfo, []).
>>
>> open_doc(Db, Id, Options) ->
>> - couch_stats_collector:increment({couchdb, database_reads}),
>> + increment_stat(Db, {couchdb, database_reads}),
>> case open_doc_int(Db, Id, Options) of
>> {ok, #doc{deleted=true}=Doc} ->
>> case lists:member(deleted, Options) of
>> @@ -157,7 +157,7 @@ find_ancestor_rev_pos({RevPos, [RevId|Re
>> end.
>>
>> open_doc_revs(Db, Id, Revs, Options) ->
>> - couch_stats_collector:increment({couchdb, database_reads}),
>> + increment_stat(Db, {couchdb, database_reads}),
>> [{ok, Results}] = open_doc_revs_int(Db, [{Id, Revs}], Options),
>> {ok, [apply_open_options(Result, Options) || Result <- Results]}.
>>
>> @@ -621,7 +621,7 @@ check_dup_atts2(_) ->
>>
>>
>> update_docs(Db, Docs, Options, replicated_changes) ->
>> - couch_stats_collector:increment({couchdb, database_writes}),
>> + increment_stat(Db, {couchdb, database_writes}),
>> DocBuckets = group_alike_docs(Docs),
>>
>> case (Db#db.validate_doc_funs /= []) orelse
>> @@ -647,7 +647,7 @@ update_docs(Db, Docs, Options, replicate
>> {ok, DocErrors};
>>
>> update_docs(Db, Docs, Options, interactive_edit) ->
>> - couch_stats_collector:increment({couchdb, database_writes}),
>> + increment_stat(Db, {couchdb, database_writes}),
>> AllOrNothing = lists:member(all_or_nothing, Options),
>> % go ahead and generate the new revision ids for the documents.
>> % separate out the NonRep documents from the rest of the documents
>> @@ -959,7 +959,12 @@ init({DbName, Filepath, Fd, Options}) ->
>> {ok, UpdaterPid} = gen_server:start_link(couch_db_updater, {self(),
>> DbName, Filepath, Fd, Options}, []),
>> {ok, #db{fd_ref_counter=RefCntr}=Db} = gen_server:call(UpdaterPid,
>> get_db),
>> couch_ref_counter:add(RefCntr),
>> - couch_stats_collector:track_process_count({couchdb, open_databases}),
>> + case lists:member(sys_db, Options) of
>> + true ->
>> + ok;
>> + false ->
>> + couch_stats_collector:track_process_count({couchdb,
>> open_databases})
>> + end,
>> process_flag(trap_exit, true),
>> {ok, Db}.
>>
>> @@ -983,7 +988,9 @@ handle_call({db_updated, NewDb}, _From,
>> couch_ref_counter:add(NewRefCntr),
>> couch_ref_counter:drop(OldRefCntr)
>> end,
>> - {reply, ok, NewDb}.
>> + {reply, ok, NewDb};
>> +handle_call(get_db, _From, Db) ->
>> + {reply, {ok, Db}, Db}.
>>
>>
>> handle_cast(Msg, Db) ->
>> @@ -1178,4 +1185,7 @@ make_doc(#db{fd=Fd}=Db, Id, Deleted, Bp,
>> }.
>>
>>
>> -
>> +increment_stat(#db{is_sys_db = true}, _Stat) ->
>> + ok;
>> +increment_stat(#db{}, Stat) ->
>> + couch_stats_collector:increment(Stat).
>>
>> Modified: couchdb/trunk/src/couchdb/couch_db.hrl
>> URL:
>> http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_db.hrl?rev=957314&r1=957313&r2=957314&view=diff
>>
>> ==============================================================================
>> --- couchdb/trunk/src/couchdb/couch_db.hrl (original)
>> +++ couchdb/trunk/src/couchdb/couch_db.hrl Wed Jun 23 19:11:50 2010
>> @@ -173,7 +173,8 @@
>> user_ctx = #user_ctx{},
>> waiting_delayed_commit = nil,
>> revs_limit = 1000,
>> - fsync_options = []
>> + fsync_options = [],
>> + is_sys_db = false
>> }).
>>
>>
>>
>> Modified: couchdb/trunk/src/couchdb/couch_db_updater.erl
>> URL:
>> http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_db_updater.erl?rev=957314&r1=957313&r2=957314&view=diff
>>
>> ==============================================================================
>> --- couchdb/trunk/src/couchdb/couch_db_updater.erl (original)
>> +++ couchdb/trunk/src/couchdb/couch_db_updater.erl Wed Jun 23 19:11:50
>> 2010
>> @@ -35,7 +35,7 @@ init({MainPid, DbName, Filepath, Fd, Opt
>>
>> Db = init_db(DbName, Filepath, Fd, Header),
>> Db2 = refresh_validate_doc_funs(Db),
>> - {ok, Db2#db{main_pid=MainPid}}.
>> + {ok, Db2#db{main_pid = MainPid, is_sys_db = lists:member(sys_db,
>> Options)}}.
>>
>>
>> terminate(_Reason, Db) ->
>>
>> Modified: couchdb/trunk/src/couchdb/couch_file.erl
>> URL:
>> http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_file.erl?rev=957314&r1=957313&r2=957314&view=diff
>>
>> ==============================================================================
>> --- couchdb/trunk/src/couchdb/couch_file.erl (original)
>> +++ couchdb/trunk/src/couchdb/couch_file.erl Wed Jun 23 19:11:50 2010
>> @@ -231,16 +231,14 @@ init({Filepath, Options, ReturnPid, Ref}
>> {ok, 0} = file:position(Fd, 0),
>> ok = file:truncate(Fd),
>> ok = file:sync(Fd),
>> - couch_stats_collector:track_process_count(
>> - {couchdb, open_os_files}),
>> + maybe_track_open_os_files(Options),
>> {ok, #file{fd=Fd}};
>> false ->
>> ok = file:close(Fd),
>> init_status_error(ReturnPid, Ref, file_exists)
>> end;
>> false ->
>> - couch_stats_collector:track_process_count(
>> - {couchdb, open_os_files}),
>> + maybe_track_open_os_files(Options),
>> {ok, #file{fd=Fd}}
>> end;
>> Error ->
>> @@ -252,7 +250,7 @@ init({Filepath, Options, ReturnPid, Ref}
>> {ok, Fd_Read} ->
>> {ok, Fd} = file:open(Filepath, [read, append, raw, binary]),
>> ok = file:close(Fd_Read),
>> - couch_stats_collector:track_process_count({couchdb,
>> open_os_files}),
>> + maybe_track_open_os_files(Options),
>> {ok, Length} = file:position(Fd, eof),
>> {ok, #file{fd=Fd, eof=Length}};
>> Error ->
>> @@ -260,6 +258,13 @@ init({Filepath, Options, ReturnPid, Ref}
>> end
>> end.
>>
>> +maybe_track_open_os_files(FileOptions) ->
>> + case lists:member(sys_db, FileOptions) of
>> + true ->
>> + ok;
>> + false ->
>> + couch_stats_collector:track_process_count({couchdb,
>> open_os_files})
>> + end.
>>
>> terminate(_Reason, _Fd) ->
>> ok.
>>
>> Modified: couchdb/trunk/src/couchdb/couch_httpd_auth.erl
>> URL:
>> http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd_auth.erl?rev=957314&r1=957313&r2=957314&view=diff
>>
>> ==============================================================================
>> --- couchdb/trunk/src/couchdb/couch_httpd_auth.erl (original)
>> +++ couchdb/trunk/src/couchdb/couch_httpd_auth.erl Wed Jun 23 19:11:50
>> 2010
>> @@ -19,7 +19,6 @@
>> -export([proxy_authentification_handler/1]).
>> -export([cookie_auth_header/2]).
>> -export([handle_session_req/1]).
>> --export([ensure_users_db_exists/1, get_user/1]).
>>
>> -import(couch_httpd, [header_value/2, send_json/2,send_json/4,
>> send_method_not_allowed/2]).
>>
>> @@ -66,7 +65,7 @@ basic_name_pw(Req) ->
>> default_authentication_handler(Req) ->
>> case basic_name_pw(Req) of
>> {User, Pass} ->
>> - case get_user(?l2b(User)) of
>> + case couch_auth_cache:get_user_creds(User) of
>> nil ->
>> throw({unauthorized, <<"Name or password is
>> incorrect.">>});
>> UserProps ->
>> @@ -156,149 +155,6 @@ proxy_auth_user(Req) ->
>> end
>> end.
>>
>> -% maybe we can use hovercraft to simplify running this view query
>> -% rename to get_user_from_users_db
>> -get_user(UserName) ->
>> - case couch_config:get("admins", ?b2l(UserName)) of
>> - "-hashed-" ++ HashedPwdAndSalt ->
>> - % the name is an admin, now check to see if there is a user doc
>> - % which has a matching name, salt, and password_sha
>> - [HashedPwd, Salt] = string:tokens(HashedPwdAndSalt, ","),
>> - case get_user_props_from_db(UserName) of
>> - nil ->
>> - [{<<"roles">>, [<<"_admin">>]},
>> - {<<"salt">>, ?l2b(Salt)},
>> - {<<"password_sha">>, ?l2b(HashedPwd)}];
>> - UserProps when is_list(UserProps) ->
>> - DocRoles = couch_util:get_value(<<"roles">>, UserProps),
>> - [{<<"roles">>, [<<"_admin">> | DocRoles]},
>> - {<<"salt">>, ?l2b(Salt)},
>> - {<<"password_sha">>, ?l2b(HashedPwd)}]
>> - end;
>> - _Else ->
>> - get_user_props_from_db(UserName)
>> - end.
>> -
>> -get_user_props_from_db(UserName) ->
>> - DbName = couch_config:get("couch_httpd_auth", "authentication_db"),
>> - {ok, Db} = ensure_users_db_exists(?l2b(DbName)),
>> - DocId = <<"org.couchdb.user:", UserName/binary>>,
>> - try couch_httpd_db:couch_doc_open(Db, DocId, nil, [conflicts]) of
>> - #doc{meta=Meta}=Doc ->
>> - % check here for conflict state and throw error if
>> conflicted
>> - case couch_util:get_value(conflicts,Meta,[]) of
>> - [] ->
>> - {DocProps} = couch_query_servers:json_doc(Doc),
>> - case couch_util:get_value(<<"type">>, DocProps) of
>> - <<"user">> ->
>> - DocProps;
>> - _Else ->
>> - ?LOG_ERROR("Invalid user doc. Id:
>> ~p",[DocId]),
>> - nil
>> - end;
>> - _Else ->
>> - throw({unauthorized, <<"User document conflict must
>> be resolved before login.">>})
>> - end
>> - catch
>> - throw:_Throw ->
>> - nil
>> - after
>> - couch_db:close(Db)
>> - end.
>> -
>> -% this should handle creating the ddoc
>> -ensure_users_db_exists(DbName) ->
>> - case couch_db:open(DbName, [{user_ctx,
>> #user_ctx{roles=[<<"_admin">>]}}]) of
>> - {ok, Db} ->
>> - ensure_auth_ddoc_exists(Db, <<"_design/_auth">>),
>> - {ok, Db};
>> - _Error ->
>> - {ok, Db} = couch_db:create(DbName, [{user_ctx,
>> #user_ctx{roles=[<<"_admin">>]}}]),
>> - ensure_auth_ddoc_exists(Db, <<"_design/_auth">>),
>> - {ok, Db}
>> - end.
>> -
>> -ensure_auth_ddoc_exists(Db, DDocId) ->
>> - try couch_httpd_db:couch_doc_open(Db, DDocId, nil, []) of
>> - _Foo -> ok
>> - catch
>> - _:_Error ->
>> - % create the design document
>> - {ok, AuthDesign} = auth_design_doc(DDocId),
>> - {ok, _Rev} = couch_db:update_doc(Db, AuthDesign, []),
>> - ok
>> - end.
>> -
>> -% add the validation function here
>> -auth_design_doc(DocId) ->
>> - DocProps = [
>> - {<<"_id">>, DocId},
>> - {<<"language">>,<<"javascript">>},
>> - {
>> - <<"validate_doc_update">>,
>> - <<"function(newDoc, oldDoc, userCtx) {
>> - if ((oldDoc || newDoc).type != 'user') {
>> - throw({forbidden : 'doc.type must be user'});
>> - } // we only validate user docs for now
>> - if (newDoc._deleted === true) {
>> - // allow deletes by admins and matching users
>> - // without checking the other fields
>> - if ((userCtx.roles.indexOf('_admin') != -1) ||
>> (userCtx.name == oldDoc.name)) {
>> - return;
>> - } else {
>> - throw({forbidden : 'Only admins may delete other
>> user docs.'});
>> - }
>> - }
>> - if (!newDoc.name) {
>> - throw({forbidden : 'doc.name is required'});
>> - }
>> - if (!(newDoc.roles && (typeof newDoc.roles.length !=
>> 'undefined') )) {
>> - throw({forbidden : 'doc.roles must be an array'});
>> - }
>> - if (newDoc._id != 'org.couchdb.user:'+newDoc.name) {
>> - throw({forbidden : 'Docid must be of the form
>> org.couchdb.user:name'});
>> - }
>> - if (oldDoc) { // validate all updates
>> - if (oldDoc.name != newDoc.name) {
>> - throw({forbidden : 'Usernames may not be
>> changed.'});
>> - }
>> - }
>> - if (newDoc.password_sha && !newDoc.salt) {
>> - throw({forbidden : 'Users with password_sha must have
>> a salt. See /_utils/script/couch.js for example code.'});
>> - }
>> - if (userCtx.roles.indexOf('_admin') == -1) { // not an
>> admin
>> - if (oldDoc) { // validate non-admin updates
>> - if (userCtx.name != newDoc.name) {
>> - throw({forbidden : 'You may only update your
>> own user document.'});
>> - }
>> - // validate role updates
>> - var oldRoles = oldDoc.roles.sort();
>> - var newRoles = newDoc.roles.sort();
>> - if (oldRoles.length != newRoles.length) {
>> - throw({forbidden : 'Only _admin may edit
>> roles'});
>> - }
>> - for (var i=0; i < oldRoles.length; i++) {
>> - if (oldRoles[i] != newRoles[i]) {
>> - throw({forbidden : 'Only _admin may edit
>> roles'});
>> - }
>> - };
>> - } else if (newDoc.roles.length > 0) {
>> - throw({forbidden : 'Only _admin may set roles'});
>> - }
>> - }
>> - // no system roles in users db
>> - for (var i=0; i < newDoc.roles.length; i++) {
>> - if (newDoc.roles[i][0] == '_') {
>> - throw({forbidden : 'No system roles (starting
>> with underscore) in users db.'});
>> - }
>> - };
>> - // no system names as names
>> - if (newDoc.name[0] == '_') {
>> - throw({forbidden : 'Username may not start with
>> underscore.'});
>> - }
>> - }">>
>> - }],
>> - {ok, couch_doc:from_json_obj({DocProps})}.
>>
>> cookie_authentication_handler(#httpd{mochi_req=MochiReq}=Req) ->
>> case MochiReq:get_cookie_value("AuthSession") of
>> @@ -321,7 +177,7 @@ cookie_authentication_handler(#httpd{moc
>> Req;
>> SecretStr ->
>> Secret = ?l2b(SecretStr),
>> - case get_user(?l2b(User)) of
>> + case couch_auth_cache:get_user_creds(User) of
>> nil -> Req;
>> UserProps ->
>> UserSalt = couch_util:get_value(<<"salt">>, UserProps,
>> <<"">>),
>> @@ -403,7 +259,7 @@ handle_session_req(#httpd{method='POST',
>> UserName = ?l2b(couch_util:get_value("name", Form, "")),
>> Password = ?l2b(couch_util:get_value("password", Form, "")),
>> ?LOG_DEBUG("Attempt Login: ~s",[UserName]),
>> - User = case get_user(UserName) of
>> + User = case couch_auth_cache:get_user_creds(UserName) of
>> nil -> [];
>> Result -> Result
>> end,
>>
>> Modified: couchdb/trunk/src/couchdb/couch_httpd_db.erl
>> URL:
>> http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd_db.erl?rev=957314&r1=957313&r2=957314&view=diff
>>
>> ==============================================================================
>> --- couchdb/trunk/src/couchdb/couch_httpd_db.erl (original)
>> +++ couchdb/trunk/src/couchdb/couch_httpd_db.erl Wed Jun 23 19:11:50 2010
>> @@ -160,23 +160,13 @@ handle_design_info_req(Req, _Db, _DDoc)
>>
>> create_db_req(#httpd{user_ctx=UserCtx}=Req, DbName) ->
>> ok = couch_httpd:verify_is_server_admin(Req),
>> - LDbName = ?b2l(DbName),
>> - case couch_config:get("couch_httpd_auth", "authentication_db") of
>> - LDbName ->
>> - % make sure user's db always has the auth ddoc
>> - {ok, Db} = couch_httpd_auth:ensure_users_db_exists(DbName),
>> - couch_db:close(Db),
>> - DbUrl = absolute_uri(Req, "/" ++
>> couch_util:url_encode(DbName)),
>> - send_json(Req, 201, [{"Location", DbUrl}], {[{ok, true}]});
>> - _Else ->
>> - case couch_server:create(DbName, [{user_ctx, UserCtx}]) of
>> - {ok, Db} ->
>> - couch_db:close(Db),
>> - DbUrl = absolute_uri(Req, "/" ++
>> couch_util:url_encode(DbName)),
>> - send_json(Req, 201, [{"Location", DbUrl}], {[{ok,
>> true}]});
>> - Error ->
>> - throw(Error)
>> - end
>> + case couch_server:create(DbName, [{user_ctx, UserCtx}]) of
>> + {ok, Db} ->
>> + couch_db:close(Db),
>> + DbUrl = absolute_uri(Req, "/" ++ couch_util:url_encode(DbName)),
>> + send_json(Req, 201, [{"Location", DbUrl}], {[{ok, true}]});
>> + Error ->
>> + throw(Error)
>> end.
>>
>> delete_db_req(#httpd{user_ctx=UserCtx}=Req, DbName) ->
>> @@ -189,15 +179,6 @@ delete_db_req(#httpd{user_ctx=UserCtx}=R
>> end.
>>
>> do_db_req(#httpd{user_ctx=UserCtx,path_parts=[DbName|_]}=Req, Fun) ->
>> - LDbName = ?b2l(DbName),
>> - % I hope this lookup is cheap.
>> - case couch_config:get("couch_httpd_auth", "authentication_db") of
>> - LDbName ->
>> - % make sure user's db always has the auth ddoc
>> - {ok, ADb} = couch_httpd_auth:ensure_users_db_exists(DbName),
>> - couch_db:close(ADb);
>> - _Else -> ok
>> - end,
>> case couch_db:open(DbName, [{user_ctx, UserCtx}]) of
>> {ok, Db} ->
>> try
>>
>> Modified: couchdb/trunk/src/couchdb/couch_httpd_oauth.erl
>> URL:
>> http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd_oauth.erl?rev=957314&r1=957313&r2=957314&view=diff
>>
>> ==============================================================================
>> --- couchdb/trunk/src/couchdb/couch_httpd_oauth.erl (original)
>> +++ couchdb/trunk/src/couchdb/couch_httpd_oauth.erl Wed Jun 23 19:11:50
>> 2010
>> @@ -41,7 +41,7 @@ set_user_ctx(Req, AccessToken) ->
>> undefined -> throw({bad_request, unknown_oauth_token});
>> Value -> ?l2b(Value)
>> end,
>> - case couch_httpd_auth:get_user(Name) of
>> + case couch_auth_cache:get_user_creds(Name) of
>> nil -> Req;
>> User ->
>> Roles = couch_util:get_value(<<"roles">>, User, []),
>>
>> Modified: couchdb/trunk/src/couchdb/couch_server.erl
>> URL:
>> http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_server.erl?rev=957314&r1=957313&r2=957314&view=diff
>>
>> ==============================================================================
>> --- couchdb/trunk/src/couchdb/couch_server.erl (original)
>> +++ couchdb/trunk/src/couchdb/couch_server.erl Wed Jun 23 19:11:50 2010
>> @@ -141,6 +141,7 @@ init([]) ->
>> ets:new(couch_dbs_by_name, [set, private, named_table]),
>> ets:new(couch_dbs_by_pid, [set, private, named_table]),
>> ets:new(couch_dbs_by_lru, [ordered_set, private, named_table]),
>> + ets:new(couch_sys_dbs, [set, private, named_table]),
>> process_flag(trap_exit, true),
>> {ok, #server{root_dir=RootDir,
>> dbname_regexp=RegExp,
>> @@ -180,7 +181,7 @@ maybe_close_lru_db(#server{dbs_open=NumO
>> end.
>>
>> try_close_lru(StartTime) ->
>> - LruTime = ets:first(couch_dbs_by_lru),
>> + LruTime = get_lru(),
>> if LruTime > StartTime ->
>> % this means we've looped through all our opened dbs and found
>> them
>> % all in use.
>> @@ -194,6 +195,7 @@ try_close_lru(StartTime) ->
>> true = ets:delete(couch_dbs_by_lru, LruTime),
>> true = ets:delete(couch_dbs_by_name, DbName),
>> true = ets:delete(couch_dbs_by_pid, MainPid),
>> + true = ets:delete(couch_sys_dbs, DbName),
>> ok;
>> false ->
>> % this still has referrers. Go ahead and give it a current lru
>> time
>> @@ -207,11 +209,31 @@ try_close_lru(StartTime) ->
>> end
>> end.
>>
>> +get_lru() ->
>> + get_lru(ets:first(couch_dbs_by_lru)).
>> +
>> +get_lru(LruTime) ->
>> + [{LruTime, DbName}] = ets:lookup(couch_dbs_by_lru, LruTime),
>> + case ets:member(couch_sys_dbs, DbName) of
>> + false ->
>> + LruTime;
>> + true ->
>> + [{_, {opened, MainPid, _}}] = ets:lookup(couch_dbs_by_name,
>> DbName),
>> + case couch_db:is_idle(MainPid) of
>> + true ->
>> + LruTime;
>> + false ->
>> + get_lru(ets:next(couch_dbs_by_lru, LruTime))
>> + end
>> + end.
>> +
>> open_async(Server, From, DbName, Filepath, Options) ->
>> Parent = self(),
>> Opener = spawn_link(fun() ->
>> Res = couch_db:start_link(DbName, Filepath, Options),
>> - gen_server:call(Parent, {open_result, DbName, Res},
>> infinity),
>> + gen_server:call(
>> + Parent, {open_result, DbName, Res, Options}, infinity
>> + ),
>> unlink(Parent),
>> case Res of
>> {ok, DbReader} ->
>> @@ -222,13 +244,20 @@ open_async(Server, From, DbName, Filepat
>> end),
>> true = ets:insert(couch_dbs_by_name, {DbName, {opening, Opener,
>> [From]}}),
>> true = ets:insert(couch_dbs_by_pid, {Opener, DbName}),
>> - Server#server{dbs_open=Server#server.dbs_open + 1}.
>> + DbsOpen = case lists:member(sys_db, Options) of
>> + true ->
>> + true = ets:insert(couch_sys_dbs, {DbName, true}),
>> + Server#server.dbs_open;
>> + false ->
>> + Server#server.dbs_open + 1
>> + end,
>> + Server#server{dbs_open = DbsOpen}.
>>
>> handle_call({set_max_dbs_open, Max}, _From, Server) ->
>> {reply, ok, Server#server{max_dbs_open=Max}};
>> handle_call(get_server, _From, Server) ->
>> {reply, {ok, Server}, Server};
>> -handle_call({open_result, DbName, {ok, OpenedDbPid}}, _From, Server) ->
>> +handle_call({open_result, DbName, {ok, OpenedDbPid}, Options}, _From,
>> Server) ->
>> link(OpenedDbPid),
>> [{DbName, {opening,Opener,Froms}}] = ets:lookup(couch_dbs_by_name,
>> DbName),
>> lists:foreach(fun({FromPid,_}=From) ->
>> @@ -241,15 +270,28 @@ handle_call({open_result, DbName, {ok, O
>> true = ets:delete(couch_dbs_by_pid, Opener),
>> true = ets:insert(couch_dbs_by_pid, {OpenedDbPid, DbName}),
>> true = ets:insert(couch_dbs_by_lru, {LruTime, DbName}),
>> + case lists:member(create, Options) of
>> + true ->
>> + couch_db_update_notifier:notify({created, DbName});
>> + false ->
>> + ok
>> + end,
>> {reply, ok, Server};
>> -handle_call({open_result, DbName, Error}, _From, Server) ->
>> +handle_call({open_result, DbName, Error, Options}, _From, Server) ->
>> [{DbName, {opening,Opener,Froms}}] = ets:lookup(couch_dbs_by_name,
>> DbName),
>> lists:foreach(fun(From) ->
>> gen_server:reply(From, Error)
>> end, Froms),
>> true = ets:delete(couch_dbs_by_name, DbName),
>> true = ets:delete(couch_dbs_by_pid, Opener),
>> - {reply, ok, Server#server{dbs_open=Server#server.dbs_open - 1}};
>> + DbsOpen = case lists:member(sys_db, Options) of
>> + true ->
>> + true = ets:delete(couch_sys_dbs, DbName),
>> + Server#server.dbs_open;
>> + false ->
>> + Server#server.dbs_open - 1
>> + end,
>> + {reply, ok, Server#server{dbs_open = DbsOpen}};
>> handle_call({open, DbName, Options}, {FromPid,_}=From, Server) ->
>> LruTime = now(),
>> case ets:lookup(couch_dbs_by_name, DbName) of
>> @@ -257,12 +299,17 @@ handle_call({open, DbName, Options}, {Fr
>> DbNameList = binary_to_list(DbName),
>> case check_dbname(Server, DbNameList) of
>> ok ->
>> - case maybe_close_lru_db(Server) of
>> - {ok, Server2} ->
>> - Filepath = get_full_filename(Server, DbNameList),
>> - {noreply, open_async(Server2, From, DbName, Filepath,
>> Options)};
>> - CloseError ->
>> - {reply, CloseError, Server}
>> + Path = get_full_filename(Server, DbNameList),
>> + case lists:member(sys_db, Options) of
>> + true ->
>> + {noreply, open_async(Server, From, DbName, Path,
>> Options)};
>> + false ->
>> + case maybe_close_lru_db(Server) of
>> + {ok, Server2} ->
>> + {noreply, open_async(Server2, From, DbName, Path,
>> Options)};
>> + CloseError ->
>> + {reply, CloseError, Server}
>> + end
>> end;
>> Error ->
>> {reply, Error, Server}
>> @@ -301,21 +348,34 @@ handle_call({delete, DbName, _Options},
>> case check_dbname(Server, DbNameList) of
>> ok ->
>> FullFilepath = get_full_filename(Server, DbNameList),
>> - Server2 =
>> + UpdateState =
>> case ets:lookup(couch_dbs_by_name, DbName) of
>> - [] -> Server;
>> + [] -> false;
>> [{_, {opening, Pid, Froms}}] ->
>> couch_util:shutdown_sync(Pid),
>> true = ets:delete(couch_dbs_by_name, DbName),
>> true = ets:delete(couch_dbs_by_pid, Pid),
>> [gen_server:send_result(F, not_found) || F <- Froms],
>> - Server#server{dbs_open=Server#server.dbs_open - 1};
>> + true;
>> [{_, {opened, Pid, LruTime}}] ->
>> couch_util:shutdown_sync(Pid),
>> true = ets:delete(couch_dbs_by_name, DbName),
>> true = ets:delete(couch_dbs_by_pid, Pid),
>> true = ets:delete(couch_dbs_by_lru, LruTime),
>> - Server#server{dbs_open=Server#server.dbs_open - 1}
>> + true
>> + end,
>> + Server2 = case UpdateState of
>> + true ->
>> + DbsOpen = case ets:member(couch_sys_dbs, DbName) of
>> + true ->
>> + true = ets:delete(couch_sys_dbs, DbName),
>> + Server#server.dbs_open;
>> + false ->
>> + Server#server.dbs_open - 1
>> + end,
>> + Server#server{dbs_open = DbsOpen};
>> + false ->
>> + Server
>> end,
>>
>> %% Delete any leftover .compact files. If we don't do this a
>> subsequent
>>
>> Modified: couchdb/trunk/src/couchdb/priv/stat_descriptions.cfg.in
>> URL:
>> http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/priv/stat_descriptions.cfg.in?rev=957314&r1=957313&r2=957314&view=diff
>>
>> ==============================================================================
>> --- couchdb/trunk/src/couchdb/priv/stat_descriptions.cfg.in (original)
>> +++ couchdb/trunk/src/couchdb/priv/stat_descriptions.cfg.in Wed Jun 23
>> 19:11:50 2010
>> @@ -19,6 +19,8 @@
>> {couchdb, open_databases, "number of open databases"}.
>> {couchdb, open_os_files, "number of file descriptors CouchDB has open"}.
>> {couchdb, request_time, "length of a request inside CouchDB without
>> MochiWeb"}.
>> +{couchdb, auth_cache_hits, "number of authentication cache hits"}.
>> +{couchdb, auth_cache_misses, "number of authentication cache misses"}.
>>
>> {httpd, bulk_requests, "number of bulk requests"}.
>> {httpd, requests, "number of HTTP requests"}.
>>
>>
>>
>
>
> --
> Filipe David Manana,
> fdmanana@gmail.com
>
> "Reasonable men adapt themselves to the world.
> Unreasonable men adapt the world to themselves.
> That's why all progress depends on unreasonable men."
>
>
--
Filipe David Manana,
fdmanana@gmail.com
"Reasonable men adapt themselves to the world.
Unreasonable men adapt the world to themselves.
That's why all progress depends on unreasonable men."
Re: svn commit: r957314 - in /couchdb/trunk: etc/couchdb/ share/ share/www/script/ share/www/script/test/ src/couchdb/ src/couchdb/priv/
Posted by Damien Katz <da...@apache.org>.
Doh, forgot to check them into svn. Fixing now!
-Damien
On Jun 23, 2010, at 12:19 PM, Filipe David Manana wrote:
> Damien,
>
> the new files share/www/script/test/auth_cache.js and src/couchdb/couch_js_functions.hrl are missing in the diff.
>
> cheers
>
> On Wed, Jun 23, 2010 at 8:11 PM, <da...@apache.org> wrote:
> Author: damien
> Date: Wed Jun 23 19:11:50 2010
> New Revision: 957314
>
> URL: http://svn.apache.org/viewvc?rev=957314&view=rev
> Log:
> Authentication caching, to avoid repeated opening and closing of the users database for each request requiring authentication. COUCHDB-807
>
> Modified:
> couchdb/trunk/etc/couchdb/default.ini.tpl.in
> couchdb/trunk/share/Makefile.am
> couchdb/trunk/share/www/script/couch_tests.js
> couchdb/trunk/share/www/script/test/users_db.js
> couchdb/trunk/src/couchdb/Makefile.am
> couchdb/trunk/src/couchdb/couch_db.erl
> couchdb/trunk/src/couchdb/couch_db.hrl
> couchdb/trunk/src/couchdb/couch_db_updater.erl
> couchdb/trunk/src/couchdb/couch_file.erl
> couchdb/trunk/src/couchdb/couch_httpd_auth.erl
> couchdb/trunk/src/couchdb/couch_httpd_db.erl
> couchdb/trunk/src/couchdb/couch_httpd_oauth.erl
> couchdb/trunk/src/couchdb/couch_server.erl
> couchdb/trunk/src/couchdb/priv/stat_descriptions.cfg.in
>
> Modified: couchdb/trunk/etc/couchdb/default.ini.tpl.in
> URL: http://svn.apache.org/viewvc/couchdb/trunk/etc/couchdb/default.ini.tpl.in?rev=957314&r1=957313&r2=957314&view=diff
> ==============================================================================
> --- couchdb/trunk/etc/couchdb/default.ini.tpl.in (original)
> +++ couchdb/trunk/etc/couchdb/default.ini.tpl.in Wed Jun 23 19:11:50 2010
> @@ -31,6 +31,7 @@ include_sasl = true
> authentication_db = _users
> require_valid_user = false
> timeout = 600 ; number of seconds before automatic logout
> +auth_cache_size = 50 ; size is number of cache entries
>
> [query_servers]
> javascript = %bindir%/%couchjs_command_name% %localbuilddatadir%/server/main.js
> @@ -55,6 +56,7 @@ httpd={couch_httpd, start_link, []}
> stats_aggregator={couch_stats_aggregator, start, []}
> stats_collector={couch_stats_collector, start, []}
> uuids={couch_uuids, start, []}
> +auth_cache={couch_auth_cache, start_link, []}
>
> [httpd_global_handlers]
> / = {couch_httpd_misc_handlers, handle_welcome_req, <<"Welcome">>}
>
> Modified: couchdb/trunk/share/Makefile.am
> URL: http://svn.apache.org/viewvc/couchdb/trunk/share/Makefile.am?rev=957314&r1=957313&r2=957314&view=diff
> ==============================================================================
> --- couchdb/trunk/share/Makefile.am (original)
> +++ couchdb/trunk/share/Makefile.am Wed Jun 23 19:11:50 2010
> @@ -110,6 +110,7 @@ nobase_dist_localdata_DATA = \
> www/script/test/attachment_names.js \
> www/script/test/attachment_paths.js \
> www/script/test/attachment_views.js \
> + www/script/test/auth_cache.js \
> www/script/test/basics.js \
> www/script/test/batch_save.js \
> www/script/test/bulk_docs.js \
>
> Modified: couchdb/trunk/share/www/script/couch_tests.js
> URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/couch_tests.js?rev=957314&r1=957313&r2=957314&view=diff
> ==============================================================================
> --- couchdb/trunk/share/www/script/couch_tests.js [utf-8] (original)
> +++ couchdb/trunk/share/www/script/couch_tests.js [utf-8] Wed Jun 23 19:11:50 2010
> @@ -35,6 +35,7 @@ loadTest("attachments_multipart.js");
> loadTest("attachment_names.js");
> loadTest("attachment_paths.js");
> loadTest("attachment_views.js");
> +loadTest("auth_cache.js");
> loadTest("batch_save.js");
> loadTest("bulk_docs.js");
> loadTest("changes.js");
>
> Modified: couchdb/trunk/share/www/script/test/users_db.js
> URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/users_db.js?rev=957314&r1=957313&r2=957314&view=diff
> ==============================================================================
> --- couchdb/trunk/share/www/script/test/users_db.js (original)
> +++ couchdb/trunk/share/www/script/test/users_db.js Wed Jun 23 19:11:50 2010
> @@ -24,8 +24,6 @@ couchTests.users_db = function(debug) {
> // to determine the actual users db name.
>
> function testFun() {
> - usersDb.deleteDb();
> -
> // test that the validation function is installed
> var ddoc = usersDb.open("_design/_auth");
> T(ddoc.validate_doc_update);
> @@ -89,10 +87,12 @@ couchTests.users_db = function(debug) {
>
> };
>
> + usersDb.deleteDb();
> run_on_modified_server(
> [{section: "couch_httpd_auth",
> - key: "authentication_db", value: "test_suite_users"}],
> + key: "authentication_db", value: usersDb.name}],
> testFun
> );
> + usersDb.deleteDb(); // cleanup
>
> }
> \ No newline at end of file
>
> Modified: couchdb/trunk/src/couchdb/Makefile.am
> URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/Makefile.am?rev=957314&r1=957313&r2=957314&view=diff
> ==============================================================================
> --- couchdb/trunk/src/couchdb/Makefile.am (original)
> +++ couchdb/trunk/src/couchdb/Makefile.am Wed Jun 23 19:11:50 2010
> @@ -17,7 +17,7 @@ couchlibdir = $(localerlanglibdir)/couch
> couchincludedir = $(couchlibdir)/include
> couchebindir = $(couchlibdir)/ebin
>
> -couchinclude_DATA = couch_db.hrl
> +couchinclude_DATA = couch_db.hrl couch_js_functions.hrl
> couchebin_DATA = $(compiled_files)
>
> # dist_devdoc_DATA = $(doc_base) $(doc_modules)
> @@ -29,6 +29,7 @@ CLEANFILES = $(compiled_files) $(doc_bas
> source_files = \
> couch.erl \
> couch_app.erl \
> + couch_auth_cache.erl \
> couch_btree.erl \
> couch_changes.erl \
> couch_config.erl \
> @@ -80,12 +81,13 @@ source_files = \
> couch_db_updater.erl \
> couch_work_queue.erl
>
> -EXTRA_DIST = $(source_files) couch_db.hrl
> +EXTRA_DIST = $(source_files) couch_db.hrl couch_js_functions.hrl
>
> compiled_files = \
> couch.app \
> couch.beam \
> couch_app.beam \
> + couch_auth_cache.beam \
> couch_btree.beam \
> couch_changes.beam \
> couch_config.beam \
> @@ -194,6 +196,6 @@ endif
>
> # $(ERL) -noshell -run edoc_run files [\"$<\"]
>
> -%.beam: %.erl couch_db.hrl
> +%.beam: %.erl couch_db.hrl couch_js_functions.hrl
> $(ERLC) $(ERLC_FLAGS) ${TEST} $<;
>
>
> Modified: couchdb/trunk/src/couchdb/couch_db.erl
> URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_db.erl?rev=957314&r1=957313&r2=957314&view=diff
> ==============================================================================
> --- couchdb/trunk/src/couchdb/couch_db.erl (original)
> +++ couchdb/trunk/src/couchdb/couch_db.erl Wed Jun 23 19:11:50 2010
> @@ -114,7 +114,7 @@ open_doc(Db, IdOrDocInfo) ->
> open_doc(Db, IdOrDocInfo, []).
>
> open_doc(Db, Id, Options) ->
> - couch_stats_collector:increment({couchdb, database_reads}),
> + increment_stat(Db, {couchdb, database_reads}),
> case open_doc_int(Db, Id, Options) of
> {ok, #doc{deleted=true}=Doc} ->
> case lists:member(deleted, Options) of
> @@ -157,7 +157,7 @@ find_ancestor_rev_pos({RevPos, [RevId|Re
> end.
>
> open_doc_revs(Db, Id, Revs, Options) ->
> - couch_stats_collector:increment({couchdb, database_reads}),
> + increment_stat(Db, {couchdb, database_reads}),
> [{ok, Results}] = open_doc_revs_int(Db, [{Id, Revs}], Options),
> {ok, [apply_open_options(Result, Options) || Result <- Results]}.
>
> @@ -621,7 +621,7 @@ check_dup_atts2(_) ->
>
>
> update_docs(Db, Docs, Options, replicated_changes) ->
> - couch_stats_collector:increment({couchdb, database_writes}),
> + increment_stat(Db, {couchdb, database_writes}),
> DocBuckets = group_alike_docs(Docs),
>
> case (Db#db.validate_doc_funs /= []) orelse
> @@ -647,7 +647,7 @@ update_docs(Db, Docs, Options, replicate
> {ok, DocErrors};
>
> update_docs(Db, Docs, Options, interactive_edit) ->
> - couch_stats_collector:increment({couchdb, database_writes}),
> + increment_stat(Db, {couchdb, database_writes}),
> AllOrNothing = lists:member(all_or_nothing, Options),
> % go ahead and generate the new revision ids for the documents.
> % separate out the NonRep documents from the rest of the documents
> @@ -959,7 +959,12 @@ init({DbName, Filepath, Fd, Options}) ->
> {ok, UpdaterPid} = gen_server:start_link(couch_db_updater, {self(), DbName, Filepath, Fd, Options}, []),
> {ok, #db{fd_ref_counter=RefCntr}=Db} = gen_server:call(UpdaterPid, get_db),
> couch_ref_counter:add(RefCntr),
> - couch_stats_collector:track_process_count({couchdb, open_databases}),
> + case lists:member(sys_db, Options) of
> + true ->
> + ok;
> + false ->
> + couch_stats_collector:track_process_count({couchdb, open_databases})
> + end,
> process_flag(trap_exit, true),
> {ok, Db}.
>
> @@ -983,7 +988,9 @@ handle_call({db_updated, NewDb}, _From,
> couch_ref_counter:add(NewRefCntr),
> couch_ref_counter:drop(OldRefCntr)
> end,
> - {reply, ok, NewDb}.
> + {reply, ok, NewDb};
> +handle_call(get_db, _From, Db) ->
> + {reply, {ok, Db}, Db}.
>
>
> handle_cast(Msg, Db) ->
> @@ -1178,4 +1185,7 @@ make_doc(#db{fd=Fd}=Db, Id, Deleted, Bp,
> }.
>
>
> -
> +increment_stat(#db{is_sys_db = true}, _Stat) ->
> + ok;
> +increment_stat(#db{}, Stat) ->
> + couch_stats_collector:increment(Stat).
>
> Modified: couchdb/trunk/src/couchdb/couch_db.hrl
> URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_db.hrl?rev=957314&r1=957313&r2=957314&view=diff
> ==============================================================================
> --- couchdb/trunk/src/couchdb/couch_db.hrl (original)
> +++ couchdb/trunk/src/couchdb/couch_db.hrl Wed Jun 23 19:11:50 2010
> @@ -173,7 +173,8 @@
> user_ctx = #user_ctx{},
> waiting_delayed_commit = nil,
> revs_limit = 1000,
> - fsync_options = []
> + fsync_options = [],
> + is_sys_db = false
> }).
>
>
>
> Modified: couchdb/trunk/src/couchdb/couch_db_updater.erl
> URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_db_updater.erl?rev=957314&r1=957313&r2=957314&view=diff
> ==============================================================================
> --- couchdb/trunk/src/couchdb/couch_db_updater.erl (original)
> +++ couchdb/trunk/src/couchdb/couch_db_updater.erl Wed Jun 23 19:11:50 2010
> @@ -35,7 +35,7 @@ init({MainPid, DbName, Filepath, Fd, Opt
>
> Db = init_db(DbName, Filepath, Fd, Header),
> Db2 = refresh_validate_doc_funs(Db),
> - {ok, Db2#db{main_pid=MainPid}}.
> + {ok, Db2#db{main_pid = MainPid, is_sys_db = lists:member(sys_db, Options)}}.
>
>
> terminate(_Reason, Db) ->
>
> Modified: couchdb/trunk/src/couchdb/couch_file.erl
> URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_file.erl?rev=957314&r1=957313&r2=957314&view=diff
> ==============================================================================
> --- couchdb/trunk/src/couchdb/couch_file.erl (original)
> +++ couchdb/trunk/src/couchdb/couch_file.erl Wed Jun 23 19:11:50 2010
> @@ -231,16 +231,14 @@ init({Filepath, Options, ReturnPid, Ref}
> {ok, 0} = file:position(Fd, 0),
> ok = file:truncate(Fd),
> ok = file:sync(Fd),
> - couch_stats_collector:track_process_count(
> - {couchdb, open_os_files}),
> + maybe_track_open_os_files(Options),
> {ok, #file{fd=Fd}};
> false ->
> ok = file:close(Fd),
> init_status_error(ReturnPid, Ref, file_exists)
> end;
> false ->
> - couch_stats_collector:track_process_count(
> - {couchdb, open_os_files}),
> + maybe_track_open_os_files(Options),
> {ok, #file{fd=Fd}}
> end;
> Error ->
> @@ -252,7 +250,7 @@ init({Filepath, Options, ReturnPid, Ref}
> {ok, Fd_Read} ->
> {ok, Fd} = file:open(Filepath, [read, append, raw, binary]),
> ok = file:close(Fd_Read),
> - couch_stats_collector:track_process_count({couchdb, open_os_files}),
> + maybe_track_open_os_files(Options),
> {ok, Length} = file:position(Fd, eof),
> {ok, #file{fd=Fd, eof=Length}};
> Error ->
> @@ -260,6 +258,13 @@ init({Filepath, Options, ReturnPid, Ref}
> end
> end.
>
> +maybe_track_open_os_files(FileOptions) ->
> + case lists:member(sys_db, FileOptions) of
> + true ->
> + ok;
> + false ->
> + couch_stats_collector:track_process_count({couchdb, open_os_files})
> + end.
>
> terminate(_Reason, _Fd) ->
> ok.
>
> Modified: couchdb/trunk/src/couchdb/couch_httpd_auth.erl
> URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd_auth.erl?rev=957314&r1=957313&r2=957314&view=diff
> ==============================================================================
> --- couchdb/trunk/src/couchdb/couch_httpd_auth.erl (original)
> +++ couchdb/trunk/src/couchdb/couch_httpd_auth.erl Wed Jun 23 19:11:50 2010
> @@ -19,7 +19,6 @@
> -export([proxy_authentification_handler/1]).
> -export([cookie_auth_header/2]).
> -export([handle_session_req/1]).
> --export([ensure_users_db_exists/1, get_user/1]).
>
> -import(couch_httpd, [header_value/2, send_json/2,send_json/4, send_method_not_allowed/2]).
>
> @@ -66,7 +65,7 @@ basic_name_pw(Req) ->
> default_authentication_handler(Req) ->
> case basic_name_pw(Req) of
> {User, Pass} ->
> - case get_user(?l2b(User)) of
> + case couch_auth_cache:get_user_creds(User) of
> nil ->
> throw({unauthorized, <<"Name or password is incorrect.">>});
> UserProps ->
> @@ -156,149 +155,6 @@ proxy_auth_user(Req) ->
> end
> end.
>
> -% maybe we can use hovercraft to simplify running this view query
> -% rename to get_user_from_users_db
> -get_user(UserName) ->
> - case couch_config:get("admins", ?b2l(UserName)) of
> - "-hashed-" ++ HashedPwdAndSalt ->
> - % the name is an admin, now check to see if there is a user doc
> - % which has a matching name, salt, and password_sha
> - [HashedPwd, Salt] = string:tokens(HashedPwdAndSalt, ","),
> - case get_user_props_from_db(UserName) of
> - nil ->
> - [{<<"roles">>, [<<"_admin">>]},
> - {<<"salt">>, ?l2b(Salt)},
> - {<<"password_sha">>, ?l2b(HashedPwd)}];
> - UserProps when is_list(UserProps) ->
> - DocRoles = couch_util:get_value(<<"roles">>, UserProps),
> - [{<<"roles">>, [<<"_admin">> | DocRoles]},
> - {<<"salt">>, ?l2b(Salt)},
> - {<<"password_sha">>, ?l2b(HashedPwd)}]
> - end;
> - _Else ->
> - get_user_props_from_db(UserName)
> - end.
> -
> -get_user_props_from_db(UserName) ->
> - DbName = couch_config:get("couch_httpd_auth", "authentication_db"),
> - {ok, Db} = ensure_users_db_exists(?l2b(DbName)),
> - DocId = <<"org.couchdb.user:", UserName/binary>>,
> - try couch_httpd_db:couch_doc_open(Db, DocId, nil, [conflicts]) of
> - #doc{meta=Meta}=Doc ->
> - % check here for conflict state and throw error if conflicted
> - case couch_util:get_value(conflicts,Meta,[]) of
> - [] ->
> - {DocProps} = couch_query_servers:json_doc(Doc),
> - case couch_util:get_value(<<"type">>, DocProps) of
> - <<"user">> ->
> - DocProps;
> - _Else ->
> - ?LOG_ERROR("Invalid user doc. Id: ~p",[DocId]),
> - nil
> - end;
> - _Else ->
> - throw({unauthorized, <<"User document conflict must be resolved before login.">>})
> - end
> - catch
> - throw:_Throw ->
> - nil
> - after
> - couch_db:close(Db)
> - end.
> -
> -% this should handle creating the ddoc
> -ensure_users_db_exists(DbName) ->
> - case couch_db:open(DbName, [{user_ctx, #user_ctx{roles=[<<"_admin">>]}}]) of
> - {ok, Db} ->
> - ensure_auth_ddoc_exists(Db, <<"_design/_auth">>),
> - {ok, Db};
> - _Error ->
> - {ok, Db} = couch_db:create(DbName, [{user_ctx, #user_ctx{roles=[<<"_admin">>]}}]),
> - ensure_auth_ddoc_exists(Db, <<"_design/_auth">>),
> - {ok, Db}
> - end.
> -
> -ensure_auth_ddoc_exists(Db, DDocId) ->
> - try couch_httpd_db:couch_doc_open(Db, DDocId, nil, []) of
> - _Foo -> ok
> - catch
> - _:_Error ->
> - % create the design document
> - {ok, AuthDesign} = auth_design_doc(DDocId),
> - {ok, _Rev} = couch_db:update_doc(Db, AuthDesign, []),
> - ok
> - end.
> -
> -% add the validation function here
> -auth_design_doc(DocId) ->
> - DocProps = [
> - {<<"_id">>, DocId},
> - {<<"language">>,<<"javascript">>},
> - {
> - <<"validate_doc_update">>,
> - <<"function(newDoc, oldDoc, userCtx) {
> - if ((oldDoc || newDoc).type != 'user') {
> - throw({forbidden : 'doc.type must be user'});
> - } // we only validate user docs for now
> - if (newDoc._deleted === true) {
> - // allow deletes by admins and matching users
> - // without checking the other fields
> - if ((userCtx.roles.indexOf('_admin') != -1) || (userCtx.name == oldDoc.name)) {
> - return;
> - } else {
> - throw({forbidden : 'Only admins may delete other user docs.'});
> - }
> - }
> - if (!newDoc.name) {
> - throw({forbidden : 'doc.name is required'});
> - }
> - if (!(newDoc.roles && (typeof newDoc.roles.length != 'undefined') )) {
> - throw({forbidden : 'doc.roles must be an array'});
> - }
> - if (newDoc._id != 'org.couchdb.user:'+newDoc.name) {
> - throw({forbidden : 'Docid must be of the form org.couchdb.user:name'});
> - }
> - if (oldDoc) { // validate all updates
> - if (oldDoc.name != newDoc.name) {
> - throw({forbidden : 'Usernames may not be changed.'});
> - }
> - }
> - if (newDoc.password_sha && !newDoc.salt) {
> - throw({forbidden : 'Users with password_sha must have a salt. See /_utils/script/couch.js for example code.'});
> - }
> - if (userCtx.roles.indexOf('_admin') == -1) { // not an admin
> - if (oldDoc) { // validate non-admin updates
> - if (userCtx.name != newDoc.name) {
> - throw({forbidden : 'You may only update your own user document.'});
> - }
> - // validate role updates
> - var oldRoles = oldDoc.roles.sort();
> - var newRoles = newDoc.roles.sort();
> - if (oldRoles.length != newRoles.length) {
> - throw({forbidden : 'Only _admin may edit roles'});
> - }
> - for (var i=0; i < oldRoles.length; i++) {
> - if (oldRoles[i] != newRoles[i]) {
> - throw({forbidden : 'Only _admin may edit roles'});
> - }
> - };
> - } else if (newDoc.roles.length > 0) {
> - throw({forbidden : 'Only _admin may set roles'});
> - }
> - }
> - // no system roles in users db
> - for (var i=0; i < newDoc.roles.length; i++) {
> - if (newDoc.roles[i][0] == '_') {
> - throw({forbidden : 'No system roles (starting with underscore) in users db.'});
> - }
> - };
> - // no system names as names
> - if (newDoc.name[0] == '_') {
> - throw({forbidden : 'Username may not start with underscore.'});
> - }
> - }">>
> - }],
> - {ok, couch_doc:from_json_obj({DocProps})}.
>
> cookie_authentication_handler(#httpd{mochi_req=MochiReq}=Req) ->
> case MochiReq:get_cookie_value("AuthSession") of
> @@ -321,7 +177,7 @@ cookie_authentication_handler(#httpd{moc
> Req;
> SecretStr ->
> Secret = ?l2b(SecretStr),
> - case get_user(?l2b(User)) of
> + case couch_auth_cache:get_user_creds(User) of
> nil -> Req;
> UserProps ->
> UserSalt = couch_util:get_value(<<"salt">>, UserProps, <<"">>),
> @@ -403,7 +259,7 @@ handle_session_req(#httpd{method='POST',
> UserName = ?l2b(couch_util:get_value("name", Form, "")),
> Password = ?l2b(couch_util:get_value("password", Form, "")),
> ?LOG_DEBUG("Attempt Login: ~s",[UserName]),
> - User = case get_user(UserName) of
> + User = case couch_auth_cache:get_user_creds(UserName) of
> nil -> [];
> Result -> Result
> end,
>
> Modified: couchdb/trunk/src/couchdb/couch_httpd_db.erl
> URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd_db.erl?rev=957314&r1=957313&r2=957314&view=diff
> ==============================================================================
> --- couchdb/trunk/src/couchdb/couch_httpd_db.erl (original)
> +++ couchdb/trunk/src/couchdb/couch_httpd_db.erl Wed Jun 23 19:11:50 2010
> @@ -160,23 +160,13 @@ handle_design_info_req(Req, _Db, _DDoc)
>
> create_db_req(#httpd{user_ctx=UserCtx}=Req, DbName) ->
> ok = couch_httpd:verify_is_server_admin(Req),
> - LDbName = ?b2l(DbName),
> - case couch_config:get("couch_httpd_auth", "authentication_db") of
> - LDbName ->
> - % make sure user's db always has the auth ddoc
> - {ok, Db} = couch_httpd_auth:ensure_users_db_exists(DbName),
> - couch_db:close(Db),
> - DbUrl = absolute_uri(Req, "/" ++ couch_util:url_encode(DbName)),
> - send_json(Req, 201, [{"Location", DbUrl}], {[{ok, true}]});
> - _Else ->
> - case couch_server:create(DbName, [{user_ctx, UserCtx}]) of
> - {ok, Db} ->
> - couch_db:close(Db),
> - DbUrl = absolute_uri(Req, "/" ++ couch_util:url_encode(DbName)),
> - send_json(Req, 201, [{"Location", DbUrl}], {[{ok, true}]});
> - Error ->
> - throw(Error)
> - end
> + case couch_server:create(DbName, [{user_ctx, UserCtx}]) of
> + {ok, Db} ->
> + couch_db:close(Db),
> + DbUrl = absolute_uri(Req, "/" ++ couch_util:url_encode(DbName)),
> + send_json(Req, 201, [{"Location", DbUrl}], {[{ok, true}]});
> + Error ->
> + throw(Error)
> end.
>
> delete_db_req(#httpd{user_ctx=UserCtx}=Req, DbName) ->
> @@ -189,15 +179,6 @@ delete_db_req(#httpd{user_ctx=UserCtx}=R
> end.
>
> do_db_req(#httpd{user_ctx=UserCtx,path_parts=[DbName|_]}=Req, Fun) ->
> - LDbName = ?b2l(DbName),
> - % I hope this lookup is cheap.
> - case couch_config:get("couch_httpd_auth", "authentication_db") of
> - LDbName ->
> - % make sure user's db always has the auth ddoc
> - {ok, ADb} = couch_httpd_auth:ensure_users_db_exists(DbName),
> - couch_db:close(ADb);
> - _Else -> ok
> - end,
> case couch_db:open(DbName, [{user_ctx, UserCtx}]) of
> {ok, Db} ->
> try
>
> Modified: couchdb/trunk/src/couchdb/couch_httpd_oauth.erl
> URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd_oauth.erl?rev=957314&r1=957313&r2=957314&view=diff
> ==============================================================================
> --- couchdb/trunk/src/couchdb/couch_httpd_oauth.erl (original)
> +++ couchdb/trunk/src/couchdb/couch_httpd_oauth.erl Wed Jun 23 19:11:50 2010
> @@ -41,7 +41,7 @@ set_user_ctx(Req, AccessToken) ->
> undefined -> throw({bad_request, unknown_oauth_token});
> Value -> ?l2b(Value)
> end,
> - case couch_httpd_auth:get_user(Name) of
> + case couch_auth_cache:get_user_creds(Name) of
> nil -> Req;
> User ->
> Roles = couch_util:get_value(<<"roles">>, User, []),
>
> Modified: couchdb/trunk/src/couchdb/couch_server.erl
> URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_server.erl?rev=957314&r1=957313&r2=957314&view=diff
> ==============================================================================
> --- couchdb/trunk/src/couchdb/couch_server.erl (original)
> +++ couchdb/trunk/src/couchdb/couch_server.erl Wed Jun 23 19:11:50 2010
> @@ -141,6 +141,7 @@ init([]) ->
> ets:new(couch_dbs_by_name, [set, private, named_table]),
> ets:new(couch_dbs_by_pid, [set, private, named_table]),
> ets:new(couch_dbs_by_lru, [ordered_set, private, named_table]),
> + ets:new(couch_sys_dbs, [set, private, named_table]),
> process_flag(trap_exit, true),
> {ok, #server{root_dir=RootDir,
> dbname_regexp=RegExp,
> @@ -180,7 +181,7 @@ maybe_close_lru_db(#server{dbs_open=NumO
> end.
>
> try_close_lru(StartTime) ->
> - LruTime = ets:first(couch_dbs_by_lru),
> + LruTime = get_lru(),
> if LruTime > StartTime ->
> % this means we've looped through all our opened dbs and found them
> % all in use.
> @@ -194,6 +195,7 @@ try_close_lru(StartTime) ->
> true = ets:delete(couch_dbs_by_lru, LruTime),
> true = ets:delete(couch_dbs_by_name, DbName),
> true = ets:delete(couch_dbs_by_pid, MainPid),
> + true = ets:delete(couch_sys_dbs, DbName),
> ok;
> false ->
> % this still has referrers. Go ahead and give it a current lru time
> @@ -207,11 +209,31 @@ try_close_lru(StartTime) ->
> end
> end.
>
> +get_lru() ->
> + get_lru(ets:first(couch_dbs_by_lru)).
> +
> +get_lru(LruTime) ->
> + [{LruTime, DbName}] = ets:lookup(couch_dbs_by_lru, LruTime),
> + case ets:member(couch_sys_dbs, DbName) of
> + false ->
> + LruTime;
> + true ->
> + [{_, {opened, MainPid, _}}] = ets:lookup(couch_dbs_by_name, DbName),
> + case couch_db:is_idle(MainPid) of
> + true ->
> + LruTime;
> + false ->
> + get_lru(ets:next(couch_dbs_by_lru, LruTime))
> + end
> + end.
> +
> open_async(Server, From, DbName, Filepath, Options) ->
> Parent = self(),
> Opener = spawn_link(fun() ->
> Res = couch_db:start_link(DbName, Filepath, Options),
> - gen_server:call(Parent, {open_result, DbName, Res}, infinity),
> + gen_server:call(
> + Parent, {open_result, DbName, Res, Options}, infinity
> + ),
> unlink(Parent),
> case Res of
> {ok, DbReader} ->
> @@ -222,13 +244,20 @@ open_async(Server, From, DbName, Filepat
> end),
> true = ets:insert(couch_dbs_by_name, {DbName, {opening, Opener, [From]}}),
> true = ets:insert(couch_dbs_by_pid, {Opener, DbName}),
> - Server#server{dbs_open=Server#server.dbs_open + 1}.
> + DbsOpen = case lists:member(sys_db, Options) of
> + true ->
> + true = ets:insert(couch_sys_dbs, {DbName, true}),
> + Server#server.dbs_open;
> + false ->
> + Server#server.dbs_open + 1
> + end,
> + Server#server{dbs_open = DbsOpen}.
>
> handle_call({set_max_dbs_open, Max}, _From, Server) ->
> {reply, ok, Server#server{max_dbs_open=Max}};
> handle_call(get_server, _From, Server) ->
> {reply, {ok, Server}, Server};
> -handle_call({open_result, DbName, {ok, OpenedDbPid}}, _From, Server) ->
> +handle_call({open_result, DbName, {ok, OpenedDbPid}, Options}, _From, Server) ->
> link(OpenedDbPid),
> [{DbName, {opening,Opener,Froms}}] = ets:lookup(couch_dbs_by_name, DbName),
> lists:foreach(fun({FromPid,_}=From) ->
> @@ -241,15 +270,28 @@ handle_call({open_result, DbName, {ok, O
> true = ets:delete(couch_dbs_by_pid, Opener),
> true = ets:insert(couch_dbs_by_pid, {OpenedDbPid, DbName}),
> true = ets:insert(couch_dbs_by_lru, {LruTime, DbName}),
> + case lists:member(create, Options) of
> + true ->
> + couch_db_update_notifier:notify({created, DbName});
> + false ->
> + ok
> + end,
> {reply, ok, Server};
> -handle_call({open_result, DbName, Error}, _From, Server) ->
> +handle_call({open_result, DbName, Error, Options}, _From, Server) ->
> [{DbName, {opening,Opener,Froms}}] = ets:lookup(couch_dbs_by_name, DbName),
> lists:foreach(fun(From) ->
> gen_server:reply(From, Error)
> end, Froms),
> true = ets:delete(couch_dbs_by_name, DbName),
> true = ets:delete(couch_dbs_by_pid, Opener),
> - {reply, ok, Server#server{dbs_open=Server#server.dbs_open - 1}};
> + DbsOpen = case lists:member(sys_db, Options) of
> + true ->
> + true = ets:delete(couch_sys_dbs, DbName),
> + Server#server.dbs_open;
> + false ->
> + Server#server.dbs_open - 1
> + end,
> + {reply, ok, Server#server{dbs_open = DbsOpen}};
> handle_call({open, DbName, Options}, {FromPid,_}=From, Server) ->
> LruTime = now(),
> case ets:lookup(couch_dbs_by_name, DbName) of
> @@ -257,12 +299,17 @@ handle_call({open, DbName, Options}, {Fr
> DbNameList = binary_to_list(DbName),
> case check_dbname(Server, DbNameList) of
> ok ->
> - case maybe_close_lru_db(Server) of
> - {ok, Server2} ->
> - Filepath = get_full_filename(Server, DbNameList),
> - {noreply, open_async(Server2, From, DbName, Filepath, Options)};
> - CloseError ->
> - {reply, CloseError, Server}
> + Path = get_full_filename(Server, DbNameList),
> + case lists:member(sys_db, Options) of
> + true ->
> + {noreply, open_async(Server, From, DbName, Path, Options)};
> + false ->
> + case maybe_close_lru_db(Server) of
> + {ok, Server2} ->
> + {noreply, open_async(Server2, From, DbName, Path, Options)};
> + CloseError ->
> + {reply, CloseError, Server}
> + end
> end;
> Error ->
> {reply, Error, Server}
> @@ -301,21 +348,34 @@ handle_call({delete, DbName, _Options},
> case check_dbname(Server, DbNameList) of
> ok ->
> FullFilepath = get_full_filename(Server, DbNameList),
> - Server2 =
> + UpdateState =
> case ets:lookup(couch_dbs_by_name, DbName) of
> - [] -> Server;
> + [] -> false;
> [{_, {opening, Pid, Froms}}] ->
> couch_util:shutdown_sync(Pid),
> true = ets:delete(couch_dbs_by_name, DbName),
> true = ets:delete(couch_dbs_by_pid, Pid),
> [gen_server:send_result(F, not_found) || F <- Froms],
> - Server#server{dbs_open=Server#server.dbs_open - 1};
> + true;
> [{_, {opened, Pid, LruTime}}] ->
> couch_util:shutdown_sync(Pid),
> true = ets:delete(couch_dbs_by_name, DbName),
> true = ets:delete(couch_dbs_by_pid, Pid),
> true = ets:delete(couch_dbs_by_lru, LruTime),
> - Server#server{dbs_open=Server#server.dbs_open - 1}
> + true
> + end,
> + Server2 = case UpdateState of
> + true ->
> + DbsOpen = case ets:member(couch_sys_dbs, DbName) of
> + true ->
> + true = ets:delete(couch_sys_dbs, DbName),
> + Server#server.dbs_open;
> + false ->
> + Server#server.dbs_open - 1
> + end,
> + Server#server{dbs_open = DbsOpen};
> + false ->
> + Server
> end,
>
> %% Delete any leftover .compact files. If we don't do this a subsequent
>
> Modified: couchdb/trunk/src/couchdb/priv/stat_descriptions.cfg.in
> URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/priv/stat_descriptions.cfg.in?rev=957314&r1=957313&r2=957314&view=diff
> ==============================================================================
> --- couchdb/trunk/src/couchdb/priv/stat_descriptions.cfg.in (original)
> +++ couchdb/trunk/src/couchdb/priv/stat_descriptions.cfg.in Wed Jun 23 19:11:50 2010
> @@ -19,6 +19,8 @@
> {couchdb, open_databases, "number of open databases"}.
> {couchdb, open_os_files, "number of file descriptors CouchDB has open"}.
> {couchdb, request_time, "length of a request inside CouchDB without MochiWeb"}.
> +{couchdb, auth_cache_hits, "number of authentication cache hits"}.
> +{couchdb, auth_cache_misses, "number of authentication cache misses"}.
>
> {httpd, bulk_requests, "number of bulk requests"}.
> {httpd, requests, "number of HTTP requests"}.
>
>
>
>
>
> --
> Filipe David Manana,
> fdmanana@gmail.com
>
> "Reasonable men adapt themselves to the world.
> Unreasonable men adapt the world to themselves.
> That's why all progress depends on unreasonable men."
>
Re: svn commit: r957314 - in /couchdb/trunk: etc/couchdb/ share/
share/www/script/ share/www/script/test/ src/couchdb/ src/couchdb/priv/
Posted by Filipe David Manana <fd...@gmail.com>.
Damien,
the new files share/www/script/test/auth_cache.js and
src/couchdb/couch_js_functions.hrl are missing in the diff.
cheers
On Wed, Jun 23, 2010 at 8:11 PM, <da...@apache.org> wrote:
> Author: damien
> Date: Wed Jun 23 19:11:50 2010
> New Revision: 957314
>
> URL: http://svn.apache.org/viewvc?rev=957314&view=rev
> Log:
> Authentication caching, to avoid repeated opening and closing of the users
> database for each request requiring authentication. COUCHDB-807
>
> Modified:
> couchdb/trunk/etc/couchdb/default.ini.tpl.in
> couchdb/trunk/share/Makefile.am
> couchdb/trunk/share/www/script/couch_tests.js
> couchdb/trunk/share/www/script/test/users_db.js
> couchdb/trunk/src/couchdb/Makefile.am
> couchdb/trunk/src/couchdb/couch_db.erl
> couchdb/trunk/src/couchdb/couch_db.hrl
> couchdb/trunk/src/couchdb/couch_db_updater.erl
> couchdb/trunk/src/couchdb/couch_file.erl
> couchdb/trunk/src/couchdb/couch_httpd_auth.erl
> couchdb/trunk/src/couchdb/couch_httpd_db.erl
> couchdb/trunk/src/couchdb/couch_httpd_oauth.erl
> couchdb/trunk/src/couchdb/couch_server.erl
> couchdb/trunk/src/couchdb/priv/stat_descriptions.cfg.in
>
> Modified: couchdb/trunk/etc/couchdb/default.ini.tpl.in
> URL:
> http://svn.apache.org/viewvc/couchdb/trunk/etc/couchdb/default.ini.tpl.in?rev=957314&r1=957313&r2=957314&view=diff
>
> ==============================================================================
> --- couchdb/trunk/etc/couchdb/default.ini.tpl.in (original)
> +++ couchdb/trunk/etc/couchdb/default.ini.tpl.in Wed Jun 23 19:11:50 2010
> @@ -31,6 +31,7 @@ include_sasl = true
> authentication_db = _users
> require_valid_user = false
> timeout = 600 ; number of seconds before automatic logout
> +auth_cache_size = 50 ; size is number of cache entries
>
> [query_servers]
> javascript = %bindir%/%couchjs_command_name%
> %localbuilddatadir%/server/main.js
> @@ -55,6 +56,7 @@ httpd={couch_httpd, start_link, []}
> stats_aggregator={couch_stats_aggregator, start, []}
> stats_collector={couch_stats_collector, start, []}
> uuids={couch_uuids, start, []}
> +auth_cache={couch_auth_cache, start_link, []}
>
> [httpd_global_handlers]
> / = {couch_httpd_misc_handlers, handle_welcome_req, <<"Welcome">>}
>
> Modified: couchdb/trunk/share/Makefile.am
> URL:
> http://svn.apache.org/viewvc/couchdb/trunk/share/Makefile.am?rev=957314&r1=957313&r2=957314&view=diff
>
> ==============================================================================
> --- couchdb/trunk/share/Makefile.am (original)
> +++ couchdb/trunk/share/Makefile.am Wed Jun 23 19:11:50 2010
> @@ -110,6 +110,7 @@ nobase_dist_localdata_DATA = \
> www/script/test/attachment_names.js \
> www/script/test/attachment_paths.js \
> www/script/test/attachment_views.js \
> + www/script/test/auth_cache.js \
> www/script/test/basics.js \
> www/script/test/batch_save.js \
> www/script/test/bulk_docs.js \
>
> Modified: couchdb/trunk/share/www/script/couch_tests.js
> URL:
> http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/couch_tests.js?rev=957314&r1=957313&r2=957314&view=diff
>
> ==============================================================================
> --- couchdb/trunk/share/www/script/couch_tests.js [utf-8] (original)
> +++ couchdb/trunk/share/www/script/couch_tests.js [utf-8] Wed Jun 23
> 19:11:50 2010
> @@ -35,6 +35,7 @@ loadTest("attachments_multipart.js");
> loadTest("attachment_names.js");
> loadTest("attachment_paths.js");
> loadTest("attachment_views.js");
> +loadTest("auth_cache.js");
> loadTest("batch_save.js");
> loadTest("bulk_docs.js");
> loadTest("changes.js");
>
> Modified: couchdb/trunk/share/www/script/test/users_db.js
> URL:
> http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/users_db.js?rev=957314&r1=957313&r2=957314&view=diff
>
> ==============================================================================
> --- couchdb/trunk/share/www/script/test/users_db.js (original)
> +++ couchdb/trunk/share/www/script/test/users_db.js Wed Jun 23 19:11:50
> 2010
> @@ -24,8 +24,6 @@ couchTests.users_db = function(debug) {
> // to determine the actual users db name.
>
> function testFun() {
> - usersDb.deleteDb();
> -
> // test that the validation function is installed
> var ddoc = usersDb.open("_design/_auth");
> T(ddoc.validate_doc_update);
> @@ -89,10 +87,12 @@ couchTests.users_db = function(debug) {
>
> };
>
> + usersDb.deleteDb();
> run_on_modified_server(
> [{section: "couch_httpd_auth",
> - key: "authentication_db", value: "test_suite_users"}],
> + key: "authentication_db", value: usersDb.name}],
> testFun
> );
> + usersDb.deleteDb(); // cleanup
>
> }
> \ No newline at end of file
>
> Modified: couchdb/trunk/src/couchdb/Makefile.am
> URL:
> http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/Makefile.am?rev=957314&r1=957313&r2=957314&view=diff
>
> ==============================================================================
> --- couchdb/trunk/src/couchdb/Makefile.am (original)
> +++ couchdb/trunk/src/couchdb/Makefile.am Wed Jun 23 19:11:50 2010
> @@ -17,7 +17,7 @@ couchlibdir = $(localerlanglibdir)/couch
> couchincludedir = $(couchlibdir)/include
> couchebindir = $(couchlibdir)/ebin
>
> -couchinclude_DATA = couch_db.hrl
> +couchinclude_DATA = couch_db.hrl couch_js_functions.hrl
> couchebin_DATA = $(compiled_files)
>
> # dist_devdoc_DATA = $(doc_base) $(doc_modules)
> @@ -29,6 +29,7 @@ CLEANFILES = $(compiled_files) $(doc_bas
> source_files = \
> couch.erl \
> couch_app.erl \
> + couch_auth_cache.erl \
> couch_btree.erl \
> couch_changes.erl \
> couch_config.erl \
> @@ -80,12 +81,13 @@ source_files = \
> couch_db_updater.erl \
> couch_work_queue.erl
>
> -EXTRA_DIST = $(source_files) couch_db.hrl
> +EXTRA_DIST = $(source_files) couch_db.hrl couch_js_functions.hrl
>
> compiled_files = \
> couch.app \
> couch.beam \
> couch_app.beam \
> + couch_auth_cache.beam \
> couch_btree.beam \
> couch_changes.beam \
> couch_config.beam \
> @@ -194,6 +196,6 @@ endif
>
> # $(ERL) -noshell -run edoc_run files [\"$<\"]
>
> -%.beam: %.erl couch_db.hrl
> +%.beam: %.erl couch_db.hrl couch_js_functions.hrl
> $(ERLC) $(ERLC_FLAGS) ${TEST} $<;
>
>
> Modified: couchdb/trunk/src/couchdb/couch_db.erl
> URL:
> http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_db.erl?rev=957314&r1=957313&r2=957314&view=diff
>
> ==============================================================================
> --- couchdb/trunk/src/couchdb/couch_db.erl (original)
> +++ couchdb/trunk/src/couchdb/couch_db.erl Wed Jun 23 19:11:50 2010
> @@ -114,7 +114,7 @@ open_doc(Db, IdOrDocInfo) ->
> open_doc(Db, IdOrDocInfo, []).
>
> open_doc(Db, Id, Options) ->
> - couch_stats_collector:increment({couchdb, database_reads}),
> + increment_stat(Db, {couchdb, database_reads}),
> case open_doc_int(Db, Id, Options) of
> {ok, #doc{deleted=true}=Doc} ->
> case lists:member(deleted, Options) of
> @@ -157,7 +157,7 @@ find_ancestor_rev_pos({RevPos, [RevId|Re
> end.
>
> open_doc_revs(Db, Id, Revs, Options) ->
> - couch_stats_collector:increment({couchdb, database_reads}),
> + increment_stat(Db, {couchdb, database_reads}),
> [{ok, Results}] = open_doc_revs_int(Db, [{Id, Revs}], Options),
> {ok, [apply_open_options(Result, Options) || Result <- Results]}.
>
> @@ -621,7 +621,7 @@ check_dup_atts2(_) ->
>
>
> update_docs(Db, Docs, Options, replicated_changes) ->
> - couch_stats_collector:increment({couchdb, database_writes}),
> + increment_stat(Db, {couchdb, database_writes}),
> DocBuckets = group_alike_docs(Docs),
>
> case (Db#db.validate_doc_funs /= []) orelse
> @@ -647,7 +647,7 @@ update_docs(Db, Docs, Options, replicate
> {ok, DocErrors};
>
> update_docs(Db, Docs, Options, interactive_edit) ->
> - couch_stats_collector:increment({couchdb, database_writes}),
> + increment_stat(Db, {couchdb, database_writes}),
> AllOrNothing = lists:member(all_or_nothing, Options),
> % go ahead and generate the new revision ids for the documents.
> % separate out the NonRep documents from the rest of the documents
> @@ -959,7 +959,12 @@ init({DbName, Filepath, Fd, Options}) ->
> {ok, UpdaterPid} = gen_server:start_link(couch_db_updater, {self(),
> DbName, Filepath, Fd, Options}, []),
> {ok, #db{fd_ref_counter=RefCntr}=Db} = gen_server:call(UpdaterPid,
> get_db),
> couch_ref_counter:add(RefCntr),
> - couch_stats_collector:track_process_count({couchdb, open_databases}),
> + case lists:member(sys_db, Options) of
> + true ->
> + ok;
> + false ->
> + couch_stats_collector:track_process_count({couchdb,
> open_databases})
> + end,
> process_flag(trap_exit, true),
> {ok, Db}.
>
> @@ -983,7 +988,9 @@ handle_call({db_updated, NewDb}, _From,
> couch_ref_counter:add(NewRefCntr),
> couch_ref_counter:drop(OldRefCntr)
> end,
> - {reply, ok, NewDb}.
> + {reply, ok, NewDb};
> +handle_call(get_db, _From, Db) ->
> + {reply, {ok, Db}, Db}.
>
>
> handle_cast(Msg, Db) ->
> @@ -1178,4 +1185,7 @@ make_doc(#db{fd=Fd}=Db, Id, Deleted, Bp,
> }.
>
>
> -
> +increment_stat(#db{is_sys_db = true}, _Stat) ->
> + ok;
> +increment_stat(#db{}, Stat) ->
> + couch_stats_collector:increment(Stat).
>
> Modified: couchdb/trunk/src/couchdb/couch_db.hrl
> URL:
> http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_db.hrl?rev=957314&r1=957313&r2=957314&view=diff
>
> ==============================================================================
> --- couchdb/trunk/src/couchdb/couch_db.hrl (original)
> +++ couchdb/trunk/src/couchdb/couch_db.hrl Wed Jun 23 19:11:50 2010
> @@ -173,7 +173,8 @@
> user_ctx = #user_ctx{},
> waiting_delayed_commit = nil,
> revs_limit = 1000,
> - fsync_options = []
> + fsync_options = [],
> + is_sys_db = false
> }).
>
>
>
> Modified: couchdb/trunk/src/couchdb/couch_db_updater.erl
> URL:
> http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_db_updater.erl?rev=957314&r1=957313&r2=957314&view=diff
>
> ==============================================================================
> --- couchdb/trunk/src/couchdb/couch_db_updater.erl (original)
> +++ couchdb/trunk/src/couchdb/couch_db_updater.erl Wed Jun 23 19:11:50 2010
> @@ -35,7 +35,7 @@ init({MainPid, DbName, Filepath, Fd, Opt
>
> Db = init_db(DbName, Filepath, Fd, Header),
> Db2 = refresh_validate_doc_funs(Db),
> - {ok, Db2#db{main_pid=MainPid}}.
> + {ok, Db2#db{main_pid = MainPid, is_sys_db = lists:member(sys_db,
> Options)}}.
>
>
> terminate(_Reason, Db) ->
>
> Modified: couchdb/trunk/src/couchdb/couch_file.erl
> URL:
> http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_file.erl?rev=957314&r1=957313&r2=957314&view=diff
>
> ==============================================================================
> --- couchdb/trunk/src/couchdb/couch_file.erl (original)
> +++ couchdb/trunk/src/couchdb/couch_file.erl Wed Jun 23 19:11:50 2010
> @@ -231,16 +231,14 @@ init({Filepath, Options, ReturnPid, Ref}
> {ok, 0} = file:position(Fd, 0),
> ok = file:truncate(Fd),
> ok = file:sync(Fd),
> - couch_stats_collector:track_process_count(
> - {couchdb, open_os_files}),
> + maybe_track_open_os_files(Options),
> {ok, #file{fd=Fd}};
> false ->
> ok = file:close(Fd),
> init_status_error(ReturnPid, Ref, file_exists)
> end;
> false ->
> - couch_stats_collector:track_process_count(
> - {couchdb, open_os_files}),
> + maybe_track_open_os_files(Options),
> {ok, #file{fd=Fd}}
> end;
> Error ->
> @@ -252,7 +250,7 @@ init({Filepath, Options, ReturnPid, Ref}
> {ok, Fd_Read} ->
> {ok, Fd} = file:open(Filepath, [read, append, raw, binary]),
> ok = file:close(Fd_Read),
> - couch_stats_collector:track_process_count({couchdb,
> open_os_files}),
> + maybe_track_open_os_files(Options),
> {ok, Length} = file:position(Fd, eof),
> {ok, #file{fd=Fd, eof=Length}};
> Error ->
> @@ -260,6 +258,13 @@ init({Filepath, Options, ReturnPid, Ref}
> end
> end.
>
> +maybe_track_open_os_files(FileOptions) ->
> + case lists:member(sys_db, FileOptions) of
> + true ->
> + ok;
> + false ->
> + couch_stats_collector:track_process_count({couchdb,
> open_os_files})
> + end.
>
> terminate(_Reason, _Fd) ->
> ok.
>
> Modified: couchdb/trunk/src/couchdb/couch_httpd_auth.erl
> URL:
> http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd_auth.erl?rev=957314&r1=957313&r2=957314&view=diff
>
> ==============================================================================
> --- couchdb/trunk/src/couchdb/couch_httpd_auth.erl (original)
> +++ couchdb/trunk/src/couchdb/couch_httpd_auth.erl Wed Jun 23 19:11:50 2010
> @@ -19,7 +19,6 @@
> -export([proxy_authentification_handler/1]).
> -export([cookie_auth_header/2]).
> -export([handle_session_req/1]).
> --export([ensure_users_db_exists/1, get_user/1]).
>
> -import(couch_httpd, [header_value/2, send_json/2,send_json/4,
> send_method_not_allowed/2]).
>
> @@ -66,7 +65,7 @@ basic_name_pw(Req) ->
> default_authentication_handler(Req) ->
> case basic_name_pw(Req) of
> {User, Pass} ->
> - case get_user(?l2b(User)) of
> + case couch_auth_cache:get_user_creds(User) of
> nil ->
> throw({unauthorized, <<"Name or password is
> incorrect.">>});
> UserProps ->
> @@ -156,149 +155,6 @@ proxy_auth_user(Req) ->
> end
> end.
>
> -% maybe we can use hovercraft to simplify running this view query
> -% rename to get_user_from_users_db
> -get_user(UserName) ->
> - case couch_config:get("admins", ?b2l(UserName)) of
> - "-hashed-" ++ HashedPwdAndSalt ->
> - % the name is an admin, now check to see if there is a user doc
> - % which has a matching name, salt, and password_sha
> - [HashedPwd, Salt] = string:tokens(HashedPwdAndSalt, ","),
> - case get_user_props_from_db(UserName) of
> - nil ->
> - [{<<"roles">>, [<<"_admin">>]},
> - {<<"salt">>, ?l2b(Salt)},
> - {<<"password_sha">>, ?l2b(HashedPwd)}];
> - UserProps when is_list(UserProps) ->
> - DocRoles = couch_util:get_value(<<"roles">>, UserProps),
> - [{<<"roles">>, [<<"_admin">> | DocRoles]},
> - {<<"salt">>, ?l2b(Salt)},
> - {<<"password_sha">>, ?l2b(HashedPwd)}]
> - end;
> - _Else ->
> - get_user_props_from_db(UserName)
> - end.
> -
> -get_user_props_from_db(UserName) ->
> - DbName = couch_config:get("couch_httpd_auth", "authentication_db"),
> - {ok, Db} = ensure_users_db_exists(?l2b(DbName)),
> - DocId = <<"org.couchdb.user:", UserName/binary>>,
> - try couch_httpd_db:couch_doc_open(Db, DocId, nil, [conflicts]) of
> - #doc{meta=Meta}=Doc ->
> - % check here for conflict state and throw error if conflicted
> - case couch_util:get_value(conflicts,Meta,[]) of
> - [] ->
> - {DocProps} = couch_query_servers:json_doc(Doc),
> - case couch_util:get_value(<<"type">>, DocProps) of
> - <<"user">> ->
> - DocProps;
> - _Else ->
> - ?LOG_ERROR("Invalid user doc. Id:
> ~p",[DocId]),
> - nil
> - end;
> - _Else ->
> - throw({unauthorized, <<"User document conflict must be
> resolved before login.">>})
> - end
> - catch
> - throw:_Throw ->
> - nil
> - after
> - couch_db:close(Db)
> - end.
> -
> -% this should handle creating the ddoc
> -ensure_users_db_exists(DbName) ->
> - case couch_db:open(DbName, [{user_ctx,
> #user_ctx{roles=[<<"_admin">>]}}]) of
> - {ok, Db} ->
> - ensure_auth_ddoc_exists(Db, <<"_design/_auth">>),
> - {ok, Db};
> - _Error ->
> - {ok, Db} = couch_db:create(DbName, [{user_ctx,
> #user_ctx{roles=[<<"_admin">>]}}]),
> - ensure_auth_ddoc_exists(Db, <<"_design/_auth">>),
> - {ok, Db}
> - end.
> -
> -ensure_auth_ddoc_exists(Db, DDocId) ->
> - try couch_httpd_db:couch_doc_open(Db, DDocId, nil, []) of
> - _Foo -> ok
> - catch
> - _:_Error ->
> - % create the design document
> - {ok, AuthDesign} = auth_design_doc(DDocId),
> - {ok, _Rev} = couch_db:update_doc(Db, AuthDesign, []),
> - ok
> - end.
> -
> -% add the validation function here
> -auth_design_doc(DocId) ->
> - DocProps = [
> - {<<"_id">>, DocId},
> - {<<"language">>,<<"javascript">>},
> - {
> - <<"validate_doc_update">>,
> - <<"function(newDoc, oldDoc, userCtx) {
> - if ((oldDoc || newDoc).type != 'user') {
> - throw({forbidden : 'doc.type must be user'});
> - } // we only validate user docs for now
> - if (newDoc._deleted === true) {
> - // allow deletes by admins and matching users
> - // without checking the other fields
> - if ((userCtx.roles.indexOf('_admin') != -1) ||
> (userCtx.name == oldDoc.name)) {
> - return;
> - } else {
> - throw({forbidden : 'Only admins may delete other
> user docs.'});
> - }
> - }
> - if (!newDoc.name) {
> - throw({forbidden : 'doc.name is required'});
> - }
> - if (!(newDoc.roles && (typeof newDoc.roles.length !=
> 'undefined') )) {
> - throw({forbidden : 'doc.roles must be an array'});
> - }
> - if (newDoc._id != 'org.couchdb.user:'+newDoc.name) {
> - throw({forbidden : 'Docid must be of the form
> org.couchdb.user:name'});
> - }
> - if (oldDoc) { // validate all updates
> - if (oldDoc.name != newDoc.name) {
> - throw({forbidden : 'Usernames may not be
> changed.'});
> - }
> - }
> - if (newDoc.password_sha && !newDoc.salt) {
> - throw({forbidden : 'Users with password_sha must have
> a salt. See /_utils/script/couch.js for example code.'});
> - }
> - if (userCtx.roles.indexOf('_admin') == -1) { // not an
> admin
> - if (oldDoc) { // validate non-admin updates
> - if (userCtx.name != newDoc.name) {
> - throw({forbidden : 'You may only update your own
> user document.'});
> - }
> - // validate role updates
> - var oldRoles = oldDoc.roles.sort();
> - var newRoles = newDoc.roles.sort();
> - if (oldRoles.length != newRoles.length) {
> - throw({forbidden : 'Only _admin may edit
> roles'});
> - }
> - for (var i=0; i < oldRoles.length; i++) {
> - if (oldRoles[i] != newRoles[i]) {
> - throw({forbidden : 'Only _admin may edit
> roles'});
> - }
> - };
> - } else if (newDoc.roles.length > 0) {
> - throw({forbidden : 'Only _admin may set roles'});
> - }
> - }
> - // no system roles in users db
> - for (var i=0; i < newDoc.roles.length; i++) {
> - if (newDoc.roles[i][0] == '_') {
> - throw({forbidden : 'No system roles (starting with
> underscore) in users db.'});
> - }
> - };
> - // no system names as names
> - if (newDoc.name[0] == '_') {
> - throw({forbidden : 'Username may not start with
> underscore.'});
> - }
> - }">>
> - }],
> - {ok, couch_doc:from_json_obj({DocProps})}.
>
> cookie_authentication_handler(#httpd{mochi_req=MochiReq}=Req) ->
> case MochiReq:get_cookie_value("AuthSession") of
> @@ -321,7 +177,7 @@ cookie_authentication_handler(#httpd{moc
> Req;
> SecretStr ->
> Secret = ?l2b(SecretStr),
> - case get_user(?l2b(User)) of
> + case couch_auth_cache:get_user_creds(User) of
> nil -> Req;
> UserProps ->
> UserSalt = couch_util:get_value(<<"salt">>, UserProps,
> <<"">>),
> @@ -403,7 +259,7 @@ handle_session_req(#httpd{method='POST',
> UserName = ?l2b(couch_util:get_value("name", Form, "")),
> Password = ?l2b(couch_util:get_value("password", Form, "")),
> ?LOG_DEBUG("Attempt Login: ~s",[UserName]),
> - User = case get_user(UserName) of
> + User = case couch_auth_cache:get_user_creds(UserName) of
> nil -> [];
> Result -> Result
> end,
>
> Modified: couchdb/trunk/src/couchdb/couch_httpd_db.erl
> URL:
> http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd_db.erl?rev=957314&r1=957313&r2=957314&view=diff
>
> ==============================================================================
> --- couchdb/trunk/src/couchdb/couch_httpd_db.erl (original)
> +++ couchdb/trunk/src/couchdb/couch_httpd_db.erl Wed Jun 23 19:11:50 2010
> @@ -160,23 +160,13 @@ handle_design_info_req(Req, _Db, _DDoc)
>
> create_db_req(#httpd{user_ctx=UserCtx}=Req, DbName) ->
> ok = couch_httpd:verify_is_server_admin(Req),
> - LDbName = ?b2l(DbName),
> - case couch_config:get("couch_httpd_auth", "authentication_db") of
> - LDbName ->
> - % make sure user's db always has the auth ddoc
> - {ok, Db} = couch_httpd_auth:ensure_users_db_exists(DbName),
> - couch_db:close(Db),
> - DbUrl = absolute_uri(Req, "/" ++
> couch_util:url_encode(DbName)),
> - send_json(Req, 201, [{"Location", DbUrl}], {[{ok, true}]});
> - _Else ->
> - case couch_server:create(DbName, [{user_ctx, UserCtx}]) of
> - {ok, Db} ->
> - couch_db:close(Db),
> - DbUrl = absolute_uri(Req, "/" ++
> couch_util:url_encode(DbName)),
> - send_json(Req, 201, [{"Location", DbUrl}], {[{ok,
> true}]});
> - Error ->
> - throw(Error)
> - end
> + case couch_server:create(DbName, [{user_ctx, UserCtx}]) of
> + {ok, Db} ->
> + couch_db:close(Db),
> + DbUrl = absolute_uri(Req, "/" ++ couch_util:url_encode(DbName)),
> + send_json(Req, 201, [{"Location", DbUrl}], {[{ok, true}]});
> + Error ->
> + throw(Error)
> end.
>
> delete_db_req(#httpd{user_ctx=UserCtx}=Req, DbName) ->
> @@ -189,15 +179,6 @@ delete_db_req(#httpd{user_ctx=UserCtx}=R
> end.
>
> do_db_req(#httpd{user_ctx=UserCtx,path_parts=[DbName|_]}=Req, Fun) ->
> - LDbName = ?b2l(DbName),
> - % I hope this lookup is cheap.
> - case couch_config:get("couch_httpd_auth", "authentication_db") of
> - LDbName ->
> - % make sure user's db always has the auth ddoc
> - {ok, ADb} = couch_httpd_auth:ensure_users_db_exists(DbName),
> - couch_db:close(ADb);
> - _Else -> ok
> - end,
> case couch_db:open(DbName, [{user_ctx, UserCtx}]) of
> {ok, Db} ->
> try
>
> Modified: couchdb/trunk/src/couchdb/couch_httpd_oauth.erl
> URL:
> http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd_oauth.erl?rev=957314&r1=957313&r2=957314&view=diff
>
> ==============================================================================
> --- couchdb/trunk/src/couchdb/couch_httpd_oauth.erl (original)
> +++ couchdb/trunk/src/couchdb/couch_httpd_oauth.erl Wed Jun 23 19:11:50
> 2010
> @@ -41,7 +41,7 @@ set_user_ctx(Req, AccessToken) ->
> undefined -> throw({bad_request, unknown_oauth_token});
> Value -> ?l2b(Value)
> end,
> - case couch_httpd_auth:get_user(Name) of
> + case couch_auth_cache:get_user_creds(Name) of
> nil -> Req;
> User ->
> Roles = couch_util:get_value(<<"roles">>, User, []),
>
> Modified: couchdb/trunk/src/couchdb/couch_server.erl
> URL:
> http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_server.erl?rev=957314&r1=957313&r2=957314&view=diff
>
> ==============================================================================
> --- couchdb/trunk/src/couchdb/couch_server.erl (original)
> +++ couchdb/trunk/src/couchdb/couch_server.erl Wed Jun 23 19:11:50 2010
> @@ -141,6 +141,7 @@ init([]) ->
> ets:new(couch_dbs_by_name, [set, private, named_table]),
> ets:new(couch_dbs_by_pid, [set, private, named_table]),
> ets:new(couch_dbs_by_lru, [ordered_set, private, named_table]),
> + ets:new(couch_sys_dbs, [set, private, named_table]),
> process_flag(trap_exit, true),
> {ok, #server{root_dir=RootDir,
> dbname_regexp=RegExp,
> @@ -180,7 +181,7 @@ maybe_close_lru_db(#server{dbs_open=NumO
> end.
>
> try_close_lru(StartTime) ->
> - LruTime = ets:first(couch_dbs_by_lru),
> + LruTime = get_lru(),
> if LruTime > StartTime ->
> % this means we've looped through all our opened dbs and found them
> % all in use.
> @@ -194,6 +195,7 @@ try_close_lru(StartTime) ->
> true = ets:delete(couch_dbs_by_lru, LruTime),
> true = ets:delete(couch_dbs_by_name, DbName),
> true = ets:delete(couch_dbs_by_pid, MainPid),
> + true = ets:delete(couch_sys_dbs, DbName),
> ok;
> false ->
> % this still has referrers. Go ahead and give it a current lru
> time
> @@ -207,11 +209,31 @@ try_close_lru(StartTime) ->
> end
> end.
>
> +get_lru() ->
> + get_lru(ets:first(couch_dbs_by_lru)).
> +
> +get_lru(LruTime) ->
> + [{LruTime, DbName}] = ets:lookup(couch_dbs_by_lru, LruTime),
> + case ets:member(couch_sys_dbs, DbName) of
> + false ->
> + LruTime;
> + true ->
> + [{_, {opened, MainPid, _}}] = ets:lookup(couch_dbs_by_name,
> DbName),
> + case couch_db:is_idle(MainPid) of
> + true ->
> + LruTime;
> + false ->
> + get_lru(ets:next(couch_dbs_by_lru, LruTime))
> + end
> + end.
> +
> open_async(Server, From, DbName, Filepath, Options) ->
> Parent = self(),
> Opener = spawn_link(fun() ->
> Res = couch_db:start_link(DbName, Filepath, Options),
> - gen_server:call(Parent, {open_result, DbName, Res}, infinity),
> + gen_server:call(
> + Parent, {open_result, DbName, Res, Options}, infinity
> + ),
> unlink(Parent),
> case Res of
> {ok, DbReader} ->
> @@ -222,13 +244,20 @@ open_async(Server, From, DbName, Filepat
> end),
> true = ets:insert(couch_dbs_by_name, {DbName, {opening, Opener,
> [From]}}),
> true = ets:insert(couch_dbs_by_pid, {Opener, DbName}),
> - Server#server{dbs_open=Server#server.dbs_open + 1}.
> + DbsOpen = case lists:member(sys_db, Options) of
> + true ->
> + true = ets:insert(couch_sys_dbs, {DbName, true}),
> + Server#server.dbs_open;
> + false ->
> + Server#server.dbs_open + 1
> + end,
> + Server#server{dbs_open = DbsOpen}.
>
> handle_call({set_max_dbs_open, Max}, _From, Server) ->
> {reply, ok, Server#server{max_dbs_open=Max}};
> handle_call(get_server, _From, Server) ->
> {reply, {ok, Server}, Server};
> -handle_call({open_result, DbName, {ok, OpenedDbPid}}, _From, Server) ->
> +handle_call({open_result, DbName, {ok, OpenedDbPid}, Options}, _From,
> Server) ->
> link(OpenedDbPid),
> [{DbName, {opening,Opener,Froms}}] = ets:lookup(couch_dbs_by_name,
> DbName),
> lists:foreach(fun({FromPid,_}=From) ->
> @@ -241,15 +270,28 @@ handle_call({open_result, DbName, {ok, O
> true = ets:delete(couch_dbs_by_pid, Opener),
> true = ets:insert(couch_dbs_by_pid, {OpenedDbPid, DbName}),
> true = ets:insert(couch_dbs_by_lru, {LruTime, DbName}),
> + case lists:member(create, Options) of
> + true ->
> + couch_db_update_notifier:notify({created, DbName});
> + false ->
> + ok
> + end,
> {reply, ok, Server};
> -handle_call({open_result, DbName, Error}, _From, Server) ->
> +handle_call({open_result, DbName, Error, Options}, _From, Server) ->
> [{DbName, {opening,Opener,Froms}}] = ets:lookup(couch_dbs_by_name,
> DbName),
> lists:foreach(fun(From) ->
> gen_server:reply(From, Error)
> end, Froms),
> true = ets:delete(couch_dbs_by_name, DbName),
> true = ets:delete(couch_dbs_by_pid, Opener),
> - {reply, ok, Server#server{dbs_open=Server#server.dbs_open - 1}};
> + DbsOpen = case lists:member(sys_db, Options) of
> + true ->
> + true = ets:delete(couch_sys_dbs, DbName),
> + Server#server.dbs_open;
> + false ->
> + Server#server.dbs_open - 1
> + end,
> + {reply, ok, Server#server{dbs_open = DbsOpen}};
> handle_call({open, DbName, Options}, {FromPid,_}=From, Server) ->
> LruTime = now(),
> case ets:lookup(couch_dbs_by_name, DbName) of
> @@ -257,12 +299,17 @@ handle_call({open, DbName, Options}, {Fr
> DbNameList = binary_to_list(DbName),
> case check_dbname(Server, DbNameList) of
> ok ->
> - case maybe_close_lru_db(Server) of
> - {ok, Server2} ->
> - Filepath = get_full_filename(Server, DbNameList),
> - {noreply, open_async(Server2, From, DbName, Filepath,
> Options)};
> - CloseError ->
> - {reply, CloseError, Server}
> + Path = get_full_filename(Server, DbNameList),
> + case lists:member(sys_db, Options) of
> + true ->
> + {noreply, open_async(Server, From, DbName, Path,
> Options)};
> + false ->
> + case maybe_close_lru_db(Server) of
> + {ok, Server2} ->
> + {noreply, open_async(Server2, From, DbName, Path,
> Options)};
> + CloseError ->
> + {reply, CloseError, Server}
> + end
> end;
> Error ->
> {reply, Error, Server}
> @@ -301,21 +348,34 @@ handle_call({delete, DbName, _Options},
> case check_dbname(Server, DbNameList) of
> ok ->
> FullFilepath = get_full_filename(Server, DbNameList),
> - Server2 =
> + UpdateState =
> case ets:lookup(couch_dbs_by_name, DbName) of
> - [] -> Server;
> + [] -> false;
> [{_, {opening, Pid, Froms}}] ->
> couch_util:shutdown_sync(Pid),
> true = ets:delete(couch_dbs_by_name, DbName),
> true = ets:delete(couch_dbs_by_pid, Pid),
> [gen_server:send_result(F, not_found) || F <- Froms],
> - Server#server{dbs_open=Server#server.dbs_open - 1};
> + true;
> [{_, {opened, Pid, LruTime}}] ->
> couch_util:shutdown_sync(Pid),
> true = ets:delete(couch_dbs_by_name, DbName),
> true = ets:delete(couch_dbs_by_pid, Pid),
> true = ets:delete(couch_dbs_by_lru, LruTime),
> - Server#server{dbs_open=Server#server.dbs_open - 1}
> + true
> + end,
> + Server2 = case UpdateState of
> + true ->
> + DbsOpen = case ets:member(couch_sys_dbs, DbName) of
> + true ->
> + true = ets:delete(couch_sys_dbs, DbName),
> + Server#server.dbs_open;
> + false ->
> + Server#server.dbs_open - 1
> + end,
> + Server#server{dbs_open = DbsOpen};
> + false ->
> + Server
> end,
>
> %% Delete any leftover .compact files. If we don't do this a
> subsequent
>
> Modified: couchdb/trunk/src/couchdb/priv/stat_descriptions.cfg.in
> URL:
> http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/priv/stat_descriptions.cfg.in?rev=957314&r1=957313&r2=957314&view=diff
>
> ==============================================================================
> --- couchdb/trunk/src/couchdb/priv/stat_descriptions.cfg.in (original)
> +++ couchdb/trunk/src/couchdb/priv/stat_descriptions.cfg.in Wed Jun 23
> 19:11:50 2010
> @@ -19,6 +19,8 @@
> {couchdb, open_databases, "number of open databases"}.
> {couchdb, open_os_files, "number of file descriptors CouchDB has open"}.
> {couchdb, request_time, "length of a request inside CouchDB without
> MochiWeb"}.
> +{couchdb, auth_cache_hits, "number of authentication cache hits"}.
> +{couchdb, auth_cache_misses, "number of authentication cache misses"}.
>
> {httpd, bulk_requests, "number of bulk requests"}.
> {httpd, requests, "number of HTTP requests"}.
>
>
>
--
Filipe David Manana,
fdmanana@gmail.com
"Reasonable men adapt themselves to the world.
Unreasonable men adapt the world to themselves.
That's why all progress depends on unreasonable men."