From 17fd3e388668be4abf091b01f8f49db887bcddb5 Mon Sep 17 00:00:00 2001 From: "Damien F. Katz" Date: Mon, 15 Mar 2010 22:00:48 +0000 Subject: 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. git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@923456 13f79535-47bb-0310-9956-ffa450edef68 --- src/couchdb/couch_db.erl | 5 +- src/couchdb/couch_server.erl | 109 ++++++++++++++++++++++++++----------------- 2 files changed, 69 insertions(+), 45 deletions(-) (limited to 'src') diff --git a/src/couchdb/couch_db.erl b/src/couchdb/couch_db.erl index e3c7eaf9..2415e37c 100644 --- a/src/couchdb/couch_db.erl +++ b/src/couchdb/couch_db.erl @@ -92,9 +92,8 @@ ensure_full_commit(#db{update_pid=UpdatePid,instance_start_time=StartTime}) -> 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). diff --git a/src/couchdb/couch_server.erl b/src/couchdb/couch_server.erl index 325d4a12..ac3a5df3 100644 --- a/src/couchdb/couch_server.erl +++ b/src/couchdb/couch_server.erl @@ -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. @@ -184,7 +184,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), @@ -197,7 +197,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}), @@ -205,11 +205,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 [] -> @@ -219,53 +252,33 @@ handle_call({open, DbName, Options}, _From, Server) -> 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; @@ -283,7 +296,14 @@ handle_call({delete, DbName, _Options}, _From, Server) -> 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), @@ -317,12 +337,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}). + -- cgit v1.2.3