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'),