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."