You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ja...@apache.org on 2010/06/14 15:36:40 UTC

svn commit: r954456 - in /couchdb/branches/0.11.x/src/couchdb: couch_db.erl couch_server.erl

Author: jan
Date: Mon Jun 14 13:36:40 2010
New Revision: 954456

URL: http://svn.apache.org/viewvc?rev=954456&view=rev
Log:
Made the opening of databases asynchronous, so that requests to open databases that are already in the open database cache do not have to wait on the file system.

Modified:
    couchdb/branches/0.11.x/src/couchdb/couch_db.erl
    couchdb/branches/0.11.x/src/couchdb/couch_server.erl

Modified: couchdb/branches/0.11.x/src/couchdb/couch_db.erl
URL: http://svn.apache.org/viewvc/couchdb/branches/0.11.x/src/couchdb/couch_db.erl?rev=954456&r1=954455&r2=954456&view=diff
==============================================================================
--- couchdb/branches/0.11.x/src/couchdb/couch_db.erl (original)
+++ couchdb/branches/0.11.x/src/couchdb/couch_db.erl Mon Jun 14 13:36:40 2010
@@ -93,9 +93,8 @@ ensure_full_commit(#db{update_pid=Update
 close(#db{fd_ref_counter=RefCntr}) ->
     couch_ref_counter:drop(RefCntr).
 
-open_ref_counted(MainPid, UserCtx) ->
-    {ok, Db} = gen_server:call(MainPid, {open_ref_count, self()}),
-    {ok, Db#db{user_ctx=UserCtx}}.
+open_ref_counted(MainPid, OpenedPid) ->
+    gen_server:call(MainPid, {open_ref_count, OpenedPid}).
 
 is_idle(MainPid) ->
     gen_server:call(MainPid, is_idle).

Modified: couchdb/branches/0.11.x/src/couchdb/couch_server.erl
URL: http://svn.apache.org/viewvc/couchdb/branches/0.11.x/src/couchdb/couch_server.erl?rev=954456&r1=954455&r2=954456&view=diff
==============================================================================
--- couchdb/branches/0.11.x/src/couchdb/couch_server.erl (original)
+++ couchdb/branches/0.11.x/src/couchdb/couch_server.erl Mon Jun 14 13:36:40 2010
@@ -52,18 +52,18 @@ sup_start_link() ->
 
 open(DbName, Options) ->
     case gen_server:call(couch_server, {open, DbName, Options}) of
-    {ok, MainPid} ->
+    {ok, Db} ->
         Ctx = proplists:get_value(user_ctx, Options, #user_ctx{}),
-        couch_db:open_ref_counted(MainPid, Ctx);
+        {ok, Db#db{user_ctx=Ctx}};
     Error ->
         Error
     end.
 
 create(DbName, Options) ->
     case gen_server:call(couch_server, {create, DbName, Options}) of
-    {ok, MainPid} ->
+    {ok, Db} ->
         Ctx = proplists:get_value(user_ctx, Options, #user_ctx{}),
-        couch_db:open_ref_counted(MainPid, Ctx);
+        {ok, Db#db{user_ctx=Ctx}};
     Error ->
         Error
     end.
@@ -186,7 +186,7 @@ try_close_lru(StartTime) ->
         {error, all_dbs_active};
     true ->
         [{_, DbName}] = ets:lookup(couch_dbs_by_lru, LruTime),
-        [{_, {MainPid, LruTime}}] = ets:lookup(couch_dbs_by_name, DbName),
+        [{_, {opened, MainPid, LruTime}}] = ets:lookup(couch_dbs_by_name, DbName),
         case couch_db:is_idle(MainPid) of
         true ->
             exit(MainPid, kill),
@@ -199,7 +199,7 @@ try_close_lru(StartTime) ->
             % this still has referrers. Go ahead and give it a current lru time
             % and try the next one in the table.
             NewLruTime = now(),
-            true = ets:insert(couch_dbs_by_name, {DbName, {MainPid, NewLruTime}}),
+            true = ets:insert(couch_dbs_by_name, {DbName, {opened, MainPid, NewLruTime}}),
             true = ets:insert(couch_dbs_by_pid, {MainPid, DbName}),
             true = ets:delete(couch_dbs_by_lru, LruTime),
             true = ets:insert(couch_dbs_by_lru, {NewLruTime, DbName}),
@@ -207,11 +207,44 @@ try_close_lru(StartTime) ->
         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),
+            unlink(Parent)
+        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}.
+
 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, DbName, Options}, _From, Server) ->
+handle_call({open_result, DbName, {ok, OpenedDbPid}}, _From, Server) ->
+    link(OpenedDbPid),
+    [{DbName, {opening,Opener,Froms}}] = ets:lookup(couch_dbs_by_name, DbName),
+    lists:foreach(fun({FromPid,_}=From) ->
+        gen_server:reply(From, 
+                catch couch_db:open_ref_counted(OpenedDbPid, FromPid))
+    end, Froms),
+    LruTime = now(),
+    true = ets:insert(couch_dbs_by_name,
+            {DbName, {opened, OpenedDbPid, LruTime}}),
+    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}),
+    {reply, ok, Server};
+handle_call({open_result, DbName, Error}, _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}};
+handle_call({open, DbName, Options}, {FromPid,_}=From, Server) ->
     LruTime = now(),
     case ets:lookup(couch_dbs_by_name, DbName) of
     [] ->
@@ -221,53 +254,33 @@ handle_call({open, DbName, Options}, _Fr
             case maybe_close_lru_db(Server) of
             {ok, Server2} ->
                 Filepath = get_full_filename(Server, DbNameList),
-                case couch_db:start_link(DbName, Filepath, Options) of
-                {ok, MainPid} ->
-                    true = ets:insert(couch_dbs_by_name, {DbName, {MainPid, LruTime}}),
-                    true = ets:insert(couch_dbs_by_pid, {MainPid, DbName}),
-                    true = ets:insert(couch_dbs_by_lru, {LruTime, DbName}),
-                    DbsOpen = Server2#server.dbs_open + 1,
-                    {reply, {ok, MainPid},
-                            Server2#server{dbs_open=DbsOpen}};
-                Error ->
-                    {reply, Error, Server2}
-                end;
+                {noreply, open_async(Server2, From, DbName, Filepath, Options)};
             CloseError ->
                 {reply, CloseError, Server}
             end;
         Error ->
             {reply, Error, Server}
         end;
-    [{_, {MainPid, PrevLruTime}}] ->
-        true = ets:insert(couch_dbs_by_name, {DbName, {MainPid, LruTime}}),
+    [{_, {opening, Opener, Froms}}] ->
+        true = ets:insert(couch_dbs_by_name, {DbName, {opening, Opener, [From|Froms]}}),
+        {noreply, Server};
+    [{_, {opened, MainPid, PrevLruTime}}] ->
+        true = ets:insert(couch_dbs_by_name, {DbName, {opened, MainPid, LruTime}}),
         true = ets:delete(couch_dbs_by_lru, PrevLruTime),
         true = ets:insert(couch_dbs_by_lru, {LruTime, DbName}),
-        {reply, {ok, MainPid}, Server}
+        {reply, couch_db:open_ref_counted(MainPid, FromPid), Server}
     end;
-handle_call({create, DbName, Options}, _From, Server) ->
+handle_call({create, DbName, Options}, From, Server) ->
     DbNameList = binary_to_list(DbName),
     case check_dbname(Server, DbNameList) of
     ok ->
-        Filepath = get_full_filename(Server, DbNameList),
-
         case ets:lookup(couch_dbs_by_name, DbName) of
         [] ->
             case maybe_close_lru_db(Server) of
             {ok, Server2} ->
-                case couch_db:start_link(DbName, Filepath, [create|Options]) of
-                {ok, MainPid} ->
-                    LruTime = now(),
-                    true = ets:insert(couch_dbs_by_name,
-                            {DbName, {MainPid, LruTime}}),
-                    true = ets:insert(couch_dbs_by_pid, {MainPid, DbName}),
-                    true = ets:insert(couch_dbs_by_lru, {LruTime, DbName}),
-                    DbsOpen = Server2#server.dbs_open + 1,
-                    couch_db_update_notifier:notify({created, DbName}),
-                    {reply, {ok, MainPid},
-                            Server2#server{dbs_open=DbsOpen}};
-                Error ->
-                    {reply, Error, Server2}
-                end;
+                Filepath = get_full_filename(Server, DbNameList),
+                {noreply, open_async(Server2, From, DbName, Filepath,
+                        [create | Options])};
             CloseError ->
                 {reply, CloseError, Server}
             end;
@@ -285,7 +298,14 @@ handle_call({delete, DbName, _Options}, 
         Server2 =
         case ets:lookup(couch_dbs_by_name, DbName) of
         [] -> Server;
-        [{_, {Pid, LruTime}}] ->
+        [{_, {opening, Pid, Froms}}] ->
+            exit(Pid, kill),
+            receive {'EXIT', Pid, _Reason} -> ok end,
+            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};
+        [{_, {opened, Pid, LruTime}}] ->
             exit(Pid, kill),
             receive {'EXIT', Pid, _Reason} -> ok end,
             true = ets:delete(couch_dbs_by_name, DbName),
@@ -319,12 +339,17 @@ code_change(_OldVsn, State, _Extra) ->
 
 handle_info({'EXIT', _Pid, config_change}, _Server) ->
     exit(kill);
-handle_info({'EXIT', Pid, _Reason}, #server{dbs_open=DbsOpen}=Server) ->
+handle_info({'EXIT', Pid, Reason}, #server{dbs_open=DbsOpen}=Server) ->
     [{Pid, DbName}] = ets:lookup(couch_dbs_by_pid, Pid),
-    [{DbName, {Pid, LruTime}}] = ets:lookup(couch_dbs_by_name, DbName),
+    case ets:lookup(couch_dbs_by_name, DbName) of
+    [{DbName, {opened, Pid, LruTime}}] ->
+        true = ets:delete(couch_dbs_by_lru, LruTime);
+    [{DbName, {opening, Pid, Froms}}] ->
+        [gen_server:reply(From, Reason) || From <- Froms]
+    end,
     true = ets:delete(couch_dbs_by_pid, Pid),
     true = ets:delete(couch_dbs_by_name, DbName),
-    true = ets:delete(couch_dbs_by_lru, LruTime),
     {noreply, Server#server{dbs_open=DbsOpen - 1}};
 handle_info(Info, _Server) ->
     exit({unknown_message, Info}).
+