From 6cc0a3fcaddb3094f8f0fcd5bd51b3e74e70d11c Mon Sep 17 00:00:00 2001 From: "Damien F. Katz" Date: Thu, 18 Dec 2008 20:30:24 +0000 Subject: Fixed problem when a crashed db can cause couch_server to crash when it attempts to interact with it. Moved the interaction from couch_server into the caller's process. git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@727811 13f79535-47bb-0310-9956-ffa450edef68 --- etc/couchdb/local_dev.ini | 3 +++ src/couchdb/couch_db.erl | 12 ++++----- src/couchdb/couch_server.erl | 64 ++++++++++++++++++++++++++------------------ 3 files changed, 47 insertions(+), 32 deletions(-) diff --git a/etc/couchdb/local_dev.ini b/etc/couchdb/local_dev.ini index f1f2b7a5..d1682d39 100644 --- a/etc/couchdb/local_dev.ini +++ b/etc/couchdb/local_dev.ini @@ -54,3 +54,6 @@ foo = bar [test] foo = bar + +[test] +foo = bar diff --git a/src/couchdb/couch_db.erl b/src/couchdb/couch_db.erl index 5a51a1ac..6684156f 100644 --- a/src/couchdb/couch_db.erl +++ b/src/couchdb/couch_db.erl @@ -14,7 +14,7 @@ -behaviour(gen_server). -export([open/2,close/1,create/2,start_compact/1,get_db_info/1]). --export([open_ref_counted/3,num_refs/1,monitor/1]). +-export([open_ref_counted/2,num_refs/1,monitor/1]). -export([update_doc/3,update_docs/4,update_docs/2,update_docs/3,delete_doc/3]). -export([get_doc_info/2,open_doc/2,open_doc/3,open_doc_revs/4]). -export([get_missing_revs/2,name/1,doc_to_tree/1,get_update_seq/1]). @@ -67,8 +67,8 @@ open(DbName, Options) -> close(#db{fd=Fd}) -> couch_file:drop_ref(Fd). -open_ref_counted(MainPid, OpeningPid, UserCtx) -> - {ok, Db} = gen_server:call(MainPid, {open_ref_counted_instance, OpeningPid}), +open_ref_counted(MainPid, UserCtx) -> + {ok, Db} = gen_server:call(MainPid, {open_ref_counted_instance, self()}), {ok, Db#db{user_ctx=UserCtx}}. num_refs(MainPid) -> @@ -357,8 +357,8 @@ make_first_doc_on_disk(Db, Id, [{_Rev, {IsDel, Sp}} |_]=DocPath) -> make_doc(Db, Id, IsDel, Sp, Revs). -write_and_commit(#db{update_pid=UpdatePid}=Db, DocBuckets, Options) -> - +write_and_commit(#db{update_pid=UpdatePid, user_ctx=Ctx}=Db, DocBuckets, + Options) -> % flush unwritten binaries to disk. DocBuckets2 = [[doc_flush_binaries(Doc, Db#db.fd) || Doc <- Bucket] || Bucket <- DocBuckets], case gen_server:call(UpdatePid, {update_docs, DocBuckets2, Options}, infinity) of @@ -366,7 +366,7 @@ write_and_commit(#db{update_pid=UpdatePid}=Db, DocBuckets, Options) -> retry -> % This can happen if the db file we wrote to was swapped out by % compaction. Retry writing to the current file - {ok, Db2} = open_ref_counted(Db#db.main_pid, self(), {[]}), + {ok, Db2} = open_ref_counted(Db#db.main_pid, Ctx), DocBuckets3 = [[doc_flush_binaries(Doc, Db2#db.fd) || Doc <- Bucket] || Bucket <- DocBuckets], % We only retry once close(Db2), diff --git a/src/couchdb/couch_server.erl b/src/couchdb/couch_server.erl index 08f71f2b..fd185bc8 100644 --- a/src/couchdb/couch_server.erl +++ b/src/couchdb/couch_server.erl @@ -63,10 +63,22 @@ sup_start_link() -> gen_server:start_link({local, couch_server}, couch_server, [], []). open(DbName, Options) -> - gen_server:call(couch_server, {open, DbName, Options}). + case gen_server:call(couch_server, {open, DbName, Options}) of + {ok, MainPid} -> + Ctx = proplists:get_value(user_ctx, Options, #user_ctx{}), + couch_db:open_ref_counted(MainPid, Ctx); + Error -> + Error + end. create(DbName, Options) -> - gen_server:call(couch_server, {create, DbName, Options}). + case gen_server:call(couch_server, {create, DbName, Options}) of + {ok, MainPid} -> + Ctx = proplists:get_value(user_ctx, Options, #user_ctx{}), + couch_db:open_ref_counted(MainPid, Ctx); + Error -> + Error + end. delete(DbName, Options) -> gen_server:call(couch_server, {delete, DbName, Options}). @@ -192,15 +204,14 @@ try_close_lru(StartTime) -> handle_call(get_server, _From, Server) -> {reply, {ok, Server}, Server}; -handle_call({open, DbName, Options}, {FromPid,_}, Server) -> +handle_call({open, DbName, Options}, _From, Server) -> DbNameList = binary_to_list(DbName), - UserCtx = proplists:get_value(user_ctx, Options, nil), case check_dbname(Server, DbNameList) of ok -> Filepath = get_full_filename(Server, DbNameList), LruTime = now(), 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, Options) of @@ -209,9 +220,8 @@ handle_call({open, DbName, Options}, {FromPid,_}, Server) -> true = ets:insert(couch_dbs_by_pid, {MainPid, DbName}), true = ets:insert(couch_dbs_by_lru, {LruTime, DbName}), DbsOpen = Server2#server.current_dbs_open + 1, - {reply, - couch_db:open_ref_counted(MainPid, FromPid, UserCtx), - Server2#server{current_dbs_open=DbsOpen}}; + {reply, {ok, MainPid}, + Server2#server{current_dbs_open=DbsOpen}}; Error -> {reply, Error, Server2} end; @@ -222,35 +232,37 @@ handle_call({open, DbName, Options}, {FromPid,_}, Server) -> true = ets:insert(couch_dbs_by_name, {DbName, {MainPid, LruTime}}), true = ets:delete(couch_dbs_by_lru, PrevLruTime), true = ets:insert(couch_dbs_by_lru, {LruTime, DbName}), - {reply, - couch_db:open_ref_counted(MainPid, FromPid, UserCtx), - Server} + {reply, {ok, MainPid}, Server} end; Error -> {reply, Error, Server} end; -handle_call({create, DbName, Options}, {FromPid,_}, Server) -> +handle_call({create, DbName, Options}, _From, Server) -> DbNameList = binary_to_list(DbName), - UserCtx = proplists:get_value(user_ctx, Options, nil), case check_dbname(Server, DbNameList) of ok -> Filepath = get_full_filename(Server, DbNameList), case ets:lookup(couch_dbs_by_name, DbName) of [] -> - 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 = Server#server.current_dbs_open + 1, - couch_db_update_notifier:notify({created, DbName}), - {reply, - couch_db:open_ref_counted(MainPid, FromPid, UserCtx), - Server#server{current_dbs_open=DbsOpen}}; - Error -> - {reply, Error, Server} + 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.current_dbs_open + 1, + couch_db_update_notifier:notify({created, DbName}), + {reply, {ok, MainPid}, + Server2#server{current_dbs_open=DbsOpen}}; + Error -> + {reply, Error, Server2} + end; + CloseError -> + {reply, CloseError, Server} end; [_AlreadyRunningDb] -> {reply, file_exists, Server} -- cgit v1.2.3