You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by da...@apache.org on 2019/10/24 18:13:16 UTC
[couchdb] branch optimize-couch-server created (now 27ffa2e)
This is an automated email from the ASF dual-hosted git repository.
davisp pushed a change to branch optimize-couch-server
in repository https://gitbox.apache.org/repos/asf/couchdb.git.
at 27ffa2e Set a high priority on couch_server
This branch includes the following new commits:
new 042fb8c Simplfiy interleaved message couch_server test
new 67b82fe Avoid file_server_2 for existance tests
new 3812862 Reduce logging calls in couch_server
new 5c96270 Track db open times in the async_open pid
new b1f996f Move more work out of couch_server's main loop
new 27ffa2e Set a high priority on couch_server
The 6 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
[couchdb] 01/06: Simplfiy interleaved message couch_server test
Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
davisp pushed a commit to branch optimize-couch-server
in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit 042fb8c74e2f7276de5628c5c4f4151613b42a90
Author: Paul J. Davis <pa...@gmail.com>
AuthorDate: Wed Sep 19 11:53:16 2018 -0500
Simplfiy interleaved message couch_server test
Previous this test relied on delete/open requests getting things into a
confused state. Now that we understand the issue we can replace the more
complicated logic by directly polutnig couch_server's message queue with
invalid messages.
---
src/couch/test/eunit/couch_server_tests.erl | 50 ++++++++++-------------------
1 file changed, 17 insertions(+), 33 deletions(-)
diff --git a/src/couch/test/eunit/couch_server_tests.erl b/src/couch/test/eunit/couch_server_tests.erl
index 530b7ef..7d50700 100644
--- a/src/couch/test/eunit/couch_server_tests.erl
+++ b/src/couch/test/eunit/couch_server_tests.erl
@@ -14,6 +14,8 @@
-include_lib("couch/include/couch_eunit.hrl").
-include_lib("couch/include/couch_db.hrl").
+
+-include("../src/couch_db_int.hrl").
-include("../src/couch_server_int.hrl").
start() ->
@@ -192,10 +194,11 @@ make_interleaved_requests({_, TestDbName}) ->
t_interleaved_create_delete_open(DbName) ->
- {CrtRef, DelRef, OpenRef} = {make_ref(), make_ref(), make_ref()},
+ {CrtRef, OpenRef} = {make_ref(), make_ref()},
CrtMsg = {'$gen_call', {self(), CrtRef}, {create, DbName, [?ADMIN_CTX]}},
- DelMsg = {'$gen_call', {self(), DelRef}, {delete, DbName, [?ADMIN_CTX]}},
- OpenMsg = {'$gen_call', {self(), OpenRef}, {open, DbName, [?ADMIN_CTX]}},
+ FakePid = spawn(fun() -> ok end),
+ OpenResult = {open_result, DbName, {ok, #db{main_pid = FakePid}}},
+ OpenResultMsg = {'$gen_call', {self(), OpenRef}, OpenResult},
% Get the current couch_server pid so we're sure
% to not end up messaging two different pids
@@ -215,48 +218,29 @@ t_interleaved_create_delete_open(DbName) ->
% our next requests and let the opener finish processing.
erlang:suspend_process(CouchServer),
- % Since couch_server is suspend, this delete request won't
- % be processed until after the opener has sent its
- % successful open response via gen_server:call/3
- CouchServer ! DelMsg,
-
- % This open request will be in the queue after the
- % delete request but before the gen_server:call/3
- % message which will establish the mixed up state
- % in the couch_dbs ets table
- CouchServer ! OpenMsg,
+ % We queue a confused open_result message in front of
+ % the correct response from the opener.
+ CouchServer ! OpenResultMsg,
- % First release the opener pid so it can continue
- % working while we tweak meck
+ % Release the opener pid so it can continue
Opener ! go,
- % Replace our expect call to meck so that the OpenMsg
- % isn't blocked on the receive
- meck:expect(couch_db, start_link, fun(Engine, DbName1, Filename, Options) ->
- meck:passthrough([Engine, DbName1, Filename, Options])
- end),
-
% Wait for the '$gen_call' message from OpenerPid to arrive
% in couch_server's mailbox
ok = wait_for_open_async_result(CouchServer, Opener),
% Now monitor and resume the couch_server and assert that
- % couch_server does not crash while processing OpenMsg
+ % couch_server does not crash while processing OpenResultMsg
CSRef = erlang:monitor(process, CouchServer),
erlang:resume_process(CouchServer),
check_monitor_not_triggered(CSRef),
- % The create response is expected to return not_found
- % due to the delete request canceling the async opener
- % pid and sending not_found to all waiters unconditionally
- ?assertEqual({CrtRef, not_found}, get_next_message()),
-
- % Our delete request was processed normally
- ?assertEqual({DelRef, ok}, get_next_message()),
+ % Our open_result message was processed and ignored
+ ?assertEqual({OpenRef, ok}, get_next_message()),
- % The db was deleted thus it should be not found
- % when we try and open it.
- ?assertMatch({OpenRef, {not_found, no_db_file}}, get_next_message()),
+ % Our create request was processed normally after we
+ % ignored the spurious open_result
+ ?assertMatch({CrtRef, {ok, _}}, get_next_message()),
% And finally assert that couch_server is still
% alive.
@@ -281,7 +265,7 @@ wait_for_open_async_result(CouchServer, Opener) ->
{_, Messages} = erlang:process_info(CouchServer, messages),
Found = lists:foldl(fun(Msg, Acc) ->
case Msg of
- {'$gen_call', {Opener, _}, {open_result, _, _, {ok, _}}} ->
+ {'$gen_call', {Opener, _}, {open_result, _, {ok, _}}} ->
true;
_ ->
Acc
[couchdb] 02/06: Avoid file_server_2 for existance tests
Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
davisp pushed a commit to branch optimize-couch-server
in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit 67b82fe206fa219cc692c39cb60a9871f7c9eb58
Author: Paul J. Davis <pa...@gmail.com>
AuthorDate: Tue Sep 18 15:38:39 2018 -0500
Avoid file_server_2 for existance tests
Its not uncommon to have file_server_2 behaving poorly so we'll avoid it
when there are calls that are made often.
---
src/couch/src/couch_bt_engine.erl | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/src/couch/src/couch_bt_engine.erl b/src/couch/src/couch_bt_engine.erl
index e3e4d0d..cd4f753 100644
--- a/src/couch/src/couch_bt_engine.erl
+++ b/src/couch/src/couch_bt_engine.erl
@@ -114,16 +114,17 @@
]).
+-include_lib("kernel/include/file.hrl").
-include_lib("couch/include/couch_db.hrl").
-include("couch_bt_engine.hrl").
exists(FilePath) ->
- case filelib:is_file(FilePath) of
+ case is_file(FilePath) of
true ->
true;
false ->
- filelib:is_file(FilePath ++ ".compact")
+ is_file(FilePath ++ ".compact")
end.
@@ -1235,3 +1236,10 @@ finish_compaction_int(#st{} = OldSt, #st{} = NewSt1) ->
{ok, NewSt2#st{
filepath = FilePath
}, undefined}.
+
+
+is_file(Path) ->
+ case file:read_file_info(Path, [raw]) of
+ {ok, #file_info{type = regular}} -> true;
+ _ -> false
+ end.
[couchdb] 05/06: Move more work out of couch_server's main loop
Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
davisp pushed a commit to branch optimize-couch-server
in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit b1f996f7992a2eac48b17ba84c1d790d33bb044d
Author: Paul J. Davis <pa...@gmail.com>
AuthorDate: Wed Sep 19 15:01:58 2018 -0500
Move more work out of couch_server's main loop
This moves the database name check and engine lookup into the open_async
process. There's no reason that this work had to happen in the main loop
so we can easily move the regex and engine lookups out of the loop to
minimize the work per handle_call even further.
---
src/couch/src/couch_server.erl | 90 ++++++++++++++++++++----------------------
1 file changed, 43 insertions(+), 47 deletions(-)
diff --git a/src/couch/src/couch_server.erl b/src/couch/src/couch_server.erl
index 21ebe9c..6480d59 100644
--- a/src/couch/src/couch_server.erl
+++ b/src/couch/src/couch_server.erl
@@ -354,11 +354,11 @@ maybe_close_lru_db(#server{lru=Lru}=Server) ->
{error, all_dbs_active}
end.
-open_async(Server, From, DbName, {Module, Filepath}, Options) ->
+open_async(Server, From, DbName, Options) ->
Parent = self(),
T0 = os:timestamp(),
Opener = spawn_link(fun() ->
- Res = couch_db:start_link(Module, DbName, Filepath, Options),
+ Res = open_async_int(Server, DbName, Options),
IsSuccess = case Res of
{ok, _} -> true;
_ -> false
@@ -378,7 +378,7 @@ open_async(Server, From, DbName, {Module, Filepath}, Options) ->
couch_stats:update_histogram([couchdb, db_open_time], Diff);
false ->
% Log unsuccessful open results
- couch_log:info("open_result error ~p for ~s", [Error, DbName])
+ couch_log:info("open_result error ~p for ~s", [Res, DbName])
end
end),
ReqType = case lists:member(create, Options) of
@@ -396,6 +396,20 @@ open_async(Server, From, DbName, {Module, Filepath}, Options) ->
true = ets:insert(couch_dbs_pid_to_name, {Opener, DbName}),
db_opened(Server, Options).
+open_async_int(Server, DbName, Options) ->
+ DbNameList = binary_to_list(DbName),
+ case check_dbname(Server, DbNameList) of
+ ok ->
+ case get_engine(Server, DbNameList, Options) of
+ {ok, {Module, FilePath}} ->
+ couch_db:start_link(Module, DbName, FilePath, Options);
+ Error2 ->
+ Error2
+ end;
+ Error1 ->
+ Error1
+ end.
+
handle_call(close_lru, _From, #server{lru=Lru} = Server) ->
case couch_lru:close(Lru) of
{true, NewLru} ->
@@ -426,7 +440,7 @@ handle_call({open_result, DbName, {ok, Db}}, {Opener, _}, Server) ->
[gen_server:reply(Waiter, {ok, Db}) || Waiter <- Waiters],
% Cancel the creation request if it exists.
case ReqType of
- {create, DbName, _Engine, _Options, CrFrom} ->
+ {create, DbName, _Options, CrFrom} ->
gen_server:reply(CrFrom, file_exists);
_ ->
ok
@@ -466,8 +480,8 @@ handle_call({open_result, DbName, Error}, {Opener, _}, Server) ->
true = ets:delete(couch_dbs, DbName),
true = ets:delete(couch_dbs_pid_to_name, Opener),
NewServer = case ReqType of
- {create, DbName, Engine, Options, CrFrom} ->
- open_async(Server, CrFrom, DbName, Engine, Options);
+ {create, DbName, Options, CrFrom} ->
+ open_async(Server, CrFrom, DbName, Options);
_ ->
Server
end,
@@ -480,18 +494,11 @@ handle_call({open_result, DbName, Error}, {Opener, _}, Server) ->
handle_call({open, DbName, Options}, From, Server) ->
case ets:lookup(couch_dbs, DbName) of
[] ->
- DbNameList = binary_to_list(DbName),
- case check_dbname(Server, DbNameList) of
- ok ->
- case make_room(Server, Options) of
- {ok, Server2} ->
- {ok, Engine} = get_engine(Server2, DbNameList),
- {noreply, open_async(Server2, From, DbName, Engine, Options)};
- CloseError ->
- {reply, CloseError, Server}
- end;
- Error ->
- {reply, Error, Server}
+ case make_room(Server, Options) of
+ {ok, Server2} ->
+ {noreply, open_async(Server2, From, DbName, Options)};
+ CloseError ->
+ {reply, CloseError, Server}
end;
[#entry{waiters = Waiters} = Entry] when is_list(Waiters) ->
true = ets:insert(couch_dbs, Entry#entry{waiters = [From | Waiters]}),
@@ -505,36 +512,25 @@ handle_call({open, DbName, Options}, From, Server) ->
{reply, {ok, Db}, Server}
end;
handle_call({create, DbName, Options}, From, Server) ->
- DbNameList = binary_to_list(DbName),
- case get_engine(Server, DbNameList, Options) of
- {ok, Engine} ->
- case check_dbname(Server, DbNameList) of
- ok ->
- case ets:lookup(couch_dbs, DbName) of
- [] ->
- case make_room(Server, Options) of
- {ok, Server2} ->
- {noreply, open_async(Server2, From, DbName, Engine,
- [create | Options])};
- CloseError ->
- {reply, CloseError, Server}
- end;
- [#entry{req_type = open} = Entry] ->
- % We're trying to create a database while someone is in
- % the middle of trying to open it. We allow one creator
- % to wait while we figure out if it'll succeed.
- CrOptions = [create | Options],
- Req = {create, DbName, Engine, CrOptions, From},
- true = ets:insert(couch_dbs, Entry#entry{req_type = Req}),
- {noreply, Server};
- [_AlreadyRunningDb] ->
- {reply, file_exists, Server}
- end;
- Error ->
- {reply, Error, Server}
+ case ets:lookup(couch_dbs, DbName) of
+ [] ->
+ case make_room(Server, Options) of
+ {ok, Server2} ->
+ CrOptions = [create | Options],
+ {noreply, open_async(Server2, From, DbName, CrOptions)};
+ CloseError ->
+ {reply, CloseError, Server}
end;
- Error ->
- {reply, Error, Server}
+ [#entry{req_type = open} = Entry] ->
+ % We're trying to create a database while someone is in
+ % the middle of trying to open it. We allow one creator
+ % to wait while we figure out if it'll succeed.
+ CrOptions = [create | Options],
+ Req = {create, DbName, CrOptions, From},
+ true = ets:insert(couch_dbs, Entry#entry{req_type = Req}),
+ {noreply, Server};
+ [_AlreadyRunningDb] ->
+ {reply, file_exists, Server}
end;
handle_call({delete, DbName, Options}, _From, Server) ->
DbNameList = binary_to_list(DbName),
[couchdb] 03/06: Reduce logging calls in couch_server
Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
davisp pushed a commit to branch optimize-couch-server
in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit 38128622f653142860c12dc2d9906bf0a9ca4c6d
Author: Paul J. Davis <pa...@gmail.com>
AuthorDate: Tue Sep 18 15:39:34 2018 -0500
Reduce logging calls in couch_server
Even when disabled these logs can take a non-trivial amount of
reductions which can add extra suspensions to couch_server's main loop.
---
src/couch/src/couch_server.erl | 19 +++++++++++++++----
1 file changed, 15 insertions(+), 4 deletions(-)
diff --git a/src/couch/src/couch_server.erl b/src/couch/src/couch_server.erl
index e42f103..dea950e 100644
--- a/src/couch/src/couch_server.erl
+++ b/src/couch/src/couch_server.erl
@@ -366,7 +366,13 @@ open_async(Server, From, DbName, {Module, Filepath}, Options) ->
ok
end,
gen_server:call(Parent, {open_result, T0, DbName, Res}, infinity),
- unlink(Parent)
+ unlink(Parent),
+ case Res of
+ {ok, _} ->
+ ok;
+ Error ->
+ couch_log:info("open_result error ~p for ~s", [Error, DbName])
+ end
end),
ReqType = case lists:member(create, Options) of
true -> create;
@@ -452,7 +458,6 @@ handle_call({open_result, _T0, DbName, Error}, {Opener, _}, Server) ->
{reply, ok, Server};
[#entry{pid = Opener, req_type = ReqType, waiters = Waiters} = Entry] ->
[gen_server:reply(Waiter, Error) || Waiter <- Waiters],
- couch_log:info("open_result error ~p for ~s", [Error, DbName]),
true = ets:delete(couch_dbs, DbName),
true = ets:delete(couch_dbs_pid_to_name, Opener),
NewServer = case ReqType of
@@ -485,7 +490,8 @@ handle_call({open, DbName, Options}, From, Server) ->
end;
[#entry{waiters = Waiters} = Entry] when is_list(Waiters) ->
true = ets:insert(couch_dbs, Entry#entry{waiters = [From | Waiters]}),
- if length(Waiters) =< 10 -> ok; true ->
+ NumWaiters = length(Waiters),
+ if NumWaiters =< 10 orelse NumWaiters rem 10 /= 0 -> ok; true ->
Fmt = "~b clients waiting to open db ~s",
couch_log:info(Fmt, [length(Waiters), DbName])
end,
@@ -625,7 +631,12 @@ handle_info({'EXIT', Pid, Reason}, Server) ->
"must be built with Erlang OTP R13B04 or higher.", [DbName]),
couch_log:error(Msg, [])
end,
- couch_log:info("db ~s died with reason ~p", [DbName, Reason]),
+ % We kill databases on purpose so there's no reason
+ % to log that fact. So we restrict logging to "interesting"
+ % reasons.
+ if Reason == normal orelse Reason == killed -> ok; true ->
+ couch_log:info("db ~s died with reason ~p", [DbName, Reason])
+ end,
if not is_list(Waiters) -> ok; true ->
[gen_server:reply(Waiter, Reason) || Waiter <- Waiters]
end,
[couchdb] 04/06: Track db open times in the async_open pid
Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
davisp pushed a commit to branch optimize-couch-server
in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit 5c96270a0ad723518cdd6938cb310bb0fbc9b298
Author: Paul J. Davis <pa...@gmail.com>
AuthorDate: Wed Sep 19 14:49:32 2018 -0500
Track db open times in the async_open pid
We can track the open latencies from the async_open process so that
couch_server does not have to perform the update in its main loop.
---
src/couch/src/couch_server.erl | 33 +++++++++++++++++++--------------
1 file changed, 19 insertions(+), 14 deletions(-)
diff --git a/src/couch/src/couch_server.erl b/src/couch/src/couch_server.erl
index dea950e..21ebe9c 100644
--- a/src/couch/src/couch_server.erl
+++ b/src/couch/src/couch_server.erl
@@ -359,18 +359,25 @@ open_async(Server, From, DbName, {Module, Filepath}, Options) ->
T0 = os:timestamp(),
Opener = spawn_link(fun() ->
Res = couch_db:start_link(Module, DbName, Filepath, Options),
- case {Res, lists:member(create, Options)} of
- {{ok, _Db}, true} ->
+ IsSuccess = case Res of
+ {ok, _} -> true;
+ _ -> false
+ end,
+ case IsSuccess andalso lists:member(create, Options) of
+ true ->
couch_event:notify(DbName, created);
- _ ->
+ false ->
ok
end,
- gen_server:call(Parent, {open_result, T0, DbName, Res}, infinity),
+ gen_server:call(Parent, {open_result, DbName, Res}, infinity),
unlink(Parent),
- case Res of
- {ok, _} ->
- ok;
- Error ->
+ case IsSuccess of
+ true ->
+ % Track latency times for successful opens
+ Diff = timer:now_diff(os:timestamp(), T0) / 1000,
+ couch_stats:update_histogram([couchdb, db_open_time], Diff);
+ false ->
+ % Log unsuccessful open results
couch_log:info("open_result error ~p for ~s", [Error, DbName])
end
end),
@@ -406,10 +413,8 @@ handle_call(reload_engines, _From, Server) ->
{reply, ok, Server#server{engines = get_configured_engines()}};
handle_call(get_server, _From, Server) ->
{reply, {ok, Server}, Server};
-handle_call({open_result, T0, DbName, {ok, Db}}, {Opener, _}, Server) ->
+handle_call({open_result, DbName, {ok, Db}}, {Opener, _}, Server) ->
true = ets:delete(couch_dbs_pid_to_name, Opener),
- OpenTime = timer:now_diff(os:timestamp(), T0) / 1000,
- couch_stats:update_histogram([couchdb, db_open_time], OpenTime),
DbPid = couch_db:get_pid(Db),
case ets:lookup(couch_dbs, DbName) of
[] ->
@@ -449,9 +454,9 @@ handle_call({open_result, T0, DbName, {ok, Db}}, {Opener, _}, Server) ->
exit(couch_db:get_pid(Db), kill),
{reply, ok, Server}
end;
-handle_call({open_result, T0, DbName, {error, eexist}}, From, Server) ->
- handle_call({open_result, T0, DbName, file_exists}, From, Server);
-handle_call({open_result, _T0, DbName, Error}, {Opener, _}, Server) ->
+handle_call({open_result, DbName, {error, eexist}}, From, Server) ->
+ handle_call({open_result, DbName, file_exists}, From, Server);
+handle_call({open_result, DbName, Error}, {Opener, _}, Server) ->
case ets:lookup(couch_dbs, DbName) of
[] ->
% db was deleted during async open
[couchdb] 06/06: Set a high priority on couch_server
Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
davisp pushed a commit to branch optimize-couch-server
in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit 27ffa2e8a21f1e770565ee85224bbbbd93cef8ee
Author: Paul J. Davis <pa...@gmail.com>
AuthorDate: Wed Sep 19 14:39:09 2018 -0500
Set a high priority on couch_server
In a VM with lots of processes couch_server can end up slowing down
purely to not being scheduled which in turn will cause its message queue
to backup. We've attempted this before but due to message passing have
never seen it have a significant effect. However, now that we're
actively avoiding synchronous message passing inside the man loop this
has a significant effect by being able to process messages as soon as it
has one in its mailbox.
---
src/couch/src/couch_server.erl | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/couch/src/couch_server.erl b/src/couch/src/couch_server.erl
index 6480d59..2b0b684 100644
--- a/src/couch/src/couch_server.erl
+++ b/src/couch/src/couch_server.erl
@@ -219,6 +219,7 @@ close_db_if_idle(DbName) ->
init([]) ->
couch_util:set_mqd_off_heap(?MODULE),
+ process_flag(priority, high),
% Mark pluggable storage engines as a supported feature
config:enable_feature('pluggable-storage-engines'),