summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Bastian <benjamin.bastian@gmail.com>2017-03-17 11:47:29 -0700
committerBenjamin Bastian <benjamin.bastian@gmail.com>2017-04-25 14:04:29 -0700
commit4fa9b4af1d06f64a51e287bdd5c11d83049f14c7 (patch)
tree0b8b2681baee498f9f1d312cd5876c8afa8a7ff0
parente5550fbd9ad3ee97cd90b01bd8162e38bd3f9299 (diff)
downloadcouchdb-4fa9b4af1d06f64a51e287bdd5c11d83049f14c7.tar.gz
Generalize couch_lru
This commit makes couch_lru take a function which closes the database, thereby making couch_lru no longer be tightly coupled to couch_server. This change is necessary to be able to use couch_lru in couch_index_server for making an LRU for view indices.
-rw-r--r--src/couch/include/couch_db.hrl6
-rw-r--r--src/couch/src/couch_lru.erl67
-rw-r--r--src/couch/src/couch_server.erl24
-rw-r--r--src/couch/test/couch_lru_tests.erl24
4 files changed, 71 insertions, 50 deletions
diff --git a/src/couch/include/couch_db.hrl b/src/couch/include/couch_db.hrl
index e7cd85d09..135a2eaef 100644
--- a/src/couch/include/couch_db.hrl
+++ b/src/couch/include/couch_db.hrl
@@ -231,6 +231,12 @@
atts = []
}).
+-record(couch_lru, {
+ count=0,
+ updates,
+ counts,
+ close_fun
+}).
-type doc() :: #doc{}.
-type ddoc() :: #doc{}.
diff --git a/src/couch/src/couch_lru.erl b/src/couch/src/couch_lru.erl
index b79286ee0..91ffdccd6 100644
--- a/src/couch/src/couch_lru.erl
+++ b/src/couch/src/couch_lru.erl
@@ -11,34 +11,36 @@
% the License.
-module(couch_lru).
--export([new/0, insert/2, update/2, close/1]).
+-export([new/1, insert/2, update/2, close/1]).
-include_lib("couch/include/couch_db.hrl").
-new() ->
+new(CloseFun) ->
Updates = ets:new(couch_lru_updates, [ordered_set]),
- Dbs = ets:new(couch_lru_dbs, [set]),
- {0, Updates, Dbs}.
+ Counts = ets:new(couch_lru_counts, [set]),
+ #couch_lru{updates=Updates, counts=Counts, close_fun=CloseFun}.
-insert(DbName, {Count, Updates, Dbs}) ->
- update(DbName, {Count, Updates, Dbs}).
+insert(Name, Lru) ->
+ update(Name, Lru).
-update(DbName, {Count, Updates, Dbs}) ->
- case ets:lookup(Dbs, DbName) of
+update(Name, Lru) ->
+ #couch_lru{counts=Counts, updates=Updates, count=Count} = Lru,
+ case ets:lookup(Counts, Name) of
[] ->
- true = ets:insert(Dbs, {DbName, Count});
- [{DbName, OldCount}] ->
- true = ets:update_element(Dbs, DbName, {2, Count}),
- true = ets:delete(Updates, {OldCount, DbName})
+ true = ets:insert(Counts, {Name, Count});
+ [{Name, OldCount}] ->
+ true = ets:update_element(Counts, Name, {2, Count}),
+ true = ets:delete(Updates, {OldCount, Name})
end,
- true = ets:insert(Updates, {{Count, DbName}}),
- {Count + 1, Updates, Dbs}.
+ true = ets:insert(Updates, {{Count, Name}}),
+ Lru#couch_lru{count=Count+1}.
-close({Count, Updates, Dbs}) ->
- case close_int(ets:next(Updates, {-1, <<>>}), Updates, Dbs) of
+close(Lru) ->
+ #couch_lru{updates=Updates} = Lru,
+ case close_int(ets:next(Updates, {-1, <<>>}), Lru) of
true ->
- {true, {Count, Updates, Dbs}};
+ {true, Lru};
false ->
false
end.
@@ -46,26 +48,19 @@ close({Count, Updates, Dbs}) ->
%% internals
-close_int('$end_of_table', _Updates, _Dbs) ->
+close_int('$end_of_table', _Lru) ->
false;
-close_int({_Count, DbName} = Key, Updates, Dbs) ->
- 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_dbs_pid_to_name, Pid),
- exit(Pid, kill),
+close_int({_Count, Name} = Key, Lru) ->
+ #couch_lru{updates=Updates, counts=Counts, close_fun=CloseFun} = Lru,
+ {Stop, Remove} = CloseFun(Name),
+ case Remove of
+ true ->
true = ets:delete(Updates, Key),
- true = ets:delete(Dbs, DbName),
- true;
+ true = ets:delete(Counts, Name);
false ->
- true = ets:update_element(couch_dbs, DbName, {#db.fd_monitor, nil}),
- couch_stats:increment_counter([couchdb, couch_server, lru_skip]),
- close_int(ets:next(Updates, Key), Updates, Dbs)
- end;
- false ->
- true = ets:delete(Updates, Key),
- true = ets:delete(Dbs, DbName),
- close_int(ets:next(Updates, Key), Updates, Dbs)
+ ok
+ end,
+ case Stop of
+ false -> close_int(ets:next(Updates, Key), Lru);
+ true -> true
end.
diff --git a/src/couch/src/couch_server.erl b/src/couch/src/couch_server.erl
index 115230029..53dc9e13a 100644
--- a/src/couch/src/couch_server.erl
+++ b/src/couch/src/couch_server.erl
@@ -20,7 +20,7 @@
-export([init/1, handle_call/3,sup_start_link/0]).
-export([handle_cast/2,code_change/3,handle_info/2,terminate/2]).
-export([dev_start/0,is_admin/2,has_admins/0,get_stats/0]).
--export([close_lru/0]).
+-export([close_lru/0, maybe_close_db/1]).
% config_listener api
-export([handle_config_change/5, handle_config_terminate/3]).
@@ -36,7 +36,7 @@
dbs_open=0,
start_time="",
update_lru_on_read=true,
- lru = couch_lru:new()
+ lru = couch_lru:new(fun maybe_close_db/1)
}).
dev_start() ->
@@ -97,6 +97,26 @@ update_lru(DbName) ->
close_lru() ->
gen_server:call(couch_server, close_lru).
+maybe_close_db(DbName) ->
+ % First element of return indicates if we should stop closing DBs from the
+ % LRU and the second element indicates if we closed a DB.
+ 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_dbs_pid_to_name, Pid),
+ exit(Pid, kill),
+ {true, true};
+ false ->
+ true = ets:update_element(couch_dbs, DbName, {#db.fd_monitor, nil}),
+ couch_stats:increment_counter([couchdb, couch_server, lru_skip]),
+ {false, false}
+ end;
+ false ->
+ {false, true}
+ end.
+
create(DbName, Options0) ->
Options = maybe_add_sys_db_callbacks(DbName, Options0),
case gen_server:call(couch_server, {create, DbName, Options}, infinity) of
diff --git a/src/couch/test/couch_lru_tests.erl b/src/couch/test/couch_lru_tests.erl
index 15598358f..02a131ca8 100644
--- a/src/couch/test/couch_lru_tests.erl
+++ b/src/couch/test/couch_lru_tests.erl
@@ -20,7 +20,7 @@ setup() ->
ok = meck:new(couch_db, [passthrough]),
ets:new(couch_dbs, [set, public, named_table, {keypos, #db.name}]),
ets:new(couch_dbs_pid_to_name, [set, public, named_table]),
- couch_lru:new().
+ couch_lru:new(fun couch_server:maybe_close_db/1).
teardown(_) ->
ets:delete(couch_dbs),
@@ -29,18 +29,18 @@ teardown(_) ->
new_test_() ->
{setup,
- fun() -> couch_lru:new() end,
+ fun() -> couch_lru:new(fun couch_server:maybe_close_db/1) end,
fun(Lru) ->
- ?_assertMatch({0, _, _}, Lru)
+ ?_assertEqual(0, Lru#couch_lru.count)
end
}.
insert_test_() ->
{setup,
- fun() -> couch_lru:new() end,
+ fun() -> couch_lru:new(fun couch_server:maybe_close_db/1) end,
fun(Lru) ->
Key = <<"test">>,
- {1, Updates, Dbs} = couch_lru:insert(Key, Lru),
+ #couch_lru{count=1, updates=Updates, counts=Dbs} = couch_lru:insert(Key, Lru),
[
?_assertEqual(1, ets_size(Dbs)),
?_assert(ets:member(Dbs, Key)),
@@ -52,11 +52,11 @@ insert_test_() ->
insert_same_test_() ->
{setup,
- fun() -> couch_lru:new() end,
+ fun() -> couch_lru:new(fun couch_server:maybe_close_db/1) end,
fun(Lru) ->
Key = <<"test">>,
- Lru1 = {1, Updates, Dbs} = couch_lru:insert(Key, Lru),
- {2, Updates, Dbs} = couch_lru:insert(Key, Lru1),
+ Lru1 = #couch_lru{count=1} = couch_lru:insert(Key, Lru),
+ #couch_lru{count=2, updates=Updates, counts=Dbs} = couch_lru:insert(Key, Lru1),
[
?_assertEqual(1, ets_size(Dbs)),
?_assert(ets:member(Dbs, Key)),
@@ -68,11 +68,11 @@ insert_same_test_() ->
update_test_() ->
{setup,
- fun() -> couch_lru:new() end,
+ fun() -> couch_lru:new(fun couch_server:maybe_close_db/1) end,
fun(Lru) ->
Key = <<"test">>,
- Lru1 = {1, Updates, Dbs} = couch_lru:update(Key, Lru),
- {2, Updates, Dbs} = couch_lru:update(Key, Lru1),
+ Lru1 = #couch_lru{count=1} = couch_lru:update(Key, Lru),
+ #couch_lru{count=2, updates=Updates, counts=Dbs} = couch_lru:update(Key, Lru1),
[
?_assertEqual(1, ets_size(Dbs)),
?_assert(ets:member(Dbs, Key)),
@@ -90,7 +90,7 @@ close_test_() ->
ok = meck:expect(couch_db, is_idle, 1, true),
{ok, Lru1} = add_record(Lru, <<"test1">>, c:pid(0, 1001, 0)),
{ok, Lru2} = add_record(Lru1, <<"test2">>, c:pid(0, 2001, 0)),
- {true, {2, Updates, Dbs}} = couch_lru:close(Lru2),
+ {true, #couch_lru{count=2, updates=Updates, counts=Dbs}} = couch_lru:close(Lru2),
[
?_assertEqual(1, ets_size(Dbs)),
?_assert(ets:member(Dbs, <<"test2">>)),