From 7c04f93ef1337405ac4440e254f3dae5d6e1189b Mon Sep 17 00:00:00 2001 From: "Paul J. Davis" Date: Thu, 13 Oct 2011 01:44:26 -0500 Subject: [PATCH] Fix badarg error in couch_server:try_close_lru/1 The race condition in couch_server's ets table usage rears its ugly head by leaving an entry in couch_lru. This patch just addresses the issue by allowing the client pid to use the db and ignores the fact that for the duration its over the max_dbs_open setting. --- apps/couch/src/couch_server.erl | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/apps/couch/src/couch_server.erl b/apps/couch/src/couch_server.erl index f9c960c5..c7850990 100644 --- a/apps/couch/src/couch_server.erl +++ b/apps/couch/src/couch_server.erl @@ -199,16 +199,28 @@ try_close_lru(StartTime) -> % There may exist an extremely small possibility of a race % condition here, if a process could lookup the DB before the lock, % but fail to monitor the fd before the is_idle check. - true = ets:update_element(couch_dbs, DbName, {#db.fd_monitor, locked}), - [#db{main_pid = Pid} = Db] = ets:lookup(couch_dbs, DbName), - case couch_db:is_idle(Db) of true -> - true = ets:delete(couch_dbs, DbName), - true = ets:delete(couch_lru, DbName), - exit(Pid, kill), - ok; + % + % If we do hit this race condition the behavior is that the process + % grabbing the database will end up inserting a value into the + % couch_lru table. Its possible that we end up picking that up + % as the DbName above to close. So we here we'll just remove the + % couch_lru entry and ignore it. + case ets:update_element(couch_dbs, DbName, {#db.fd_monitor, locked}) of + true -> + [#db{main_pid = Pid} = Db] = ets:lookup(couch_dbs, DbName), + case couch_db:is_idle(Db) of true -> + true = ets:delete(couch_dbs, DbName), + true = ets:delete(couch_lru, DbName), + exit(Pid, kill), + ok; + false -> + Update = {#db.fd_monitor, nil}, + true = ets:update_element(couch_dbs, DbName, Update), + true = ets:insert(couch_lru, {DbName, now()}), + try_close_lru(StartTime) + end; false -> - true = ets:update_element(couch_dbs, DbName, {#db.fd_monitor, nil}), - true = ets:insert(couch_lru, {DbName, now()}), + true = ets:delete(couch_lru, DbName), try_close_lru(StartTime) end end.