summaryrefslogtreecommitdiff
path: root/src/couch/test/eunit/couchdb_os_proc_pool.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/couch/test/eunit/couchdb_os_proc_pool.erl')
-rw-r--r--src/couch/test/eunit/couchdb_os_proc_pool.erl306
1 files changed, 306 insertions, 0 deletions
diff --git a/src/couch/test/eunit/couchdb_os_proc_pool.erl b/src/couch/test/eunit/couchdb_os_proc_pool.erl
new file mode 100644
index 000000000..69f8051ad
--- /dev/null
+++ b/src/couch/test/eunit/couchdb_os_proc_pool.erl
@@ -0,0 +1,306 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+% http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couchdb_os_proc_pool).
+
+-include_lib("couch/include/couch_eunit.hrl").
+-include_lib("couch/include/couch_db.hrl").
+
+-define(TIMEOUT, 1000).
+
+
+setup() ->
+ ok = couch_proc_manager:reload(),
+ ok = setup_config().
+
+teardown(_) ->
+ ok.
+
+os_proc_pool_test_() ->
+ {
+ "OS processes pool tests",
+ {
+ setup,
+ fun test_util:start_couch/0, fun test_util:stop_couch/1,
+ {
+ foreach,
+ fun setup/0, fun teardown/1,
+ [
+ should_block_new_proc_on_full_pool(),
+ should_free_slot_on_proc_unexpected_exit(),
+ should_reuse_known_proc(),
+% should_process_waiting_queue_as_fifo(),
+ should_reduce_pool_on_idle_os_procs()
+ ]
+ }
+ }
+ }.
+
+
+should_block_new_proc_on_full_pool() ->
+ ?_test(begin
+ Client1 = spawn_client(),
+ Client2 = spawn_client(),
+ Client3 = spawn_client(),
+
+ ?assertEqual(ok, ping_client(Client1)),
+ ?assertEqual(ok, ping_client(Client2)),
+ ?assertEqual(ok, ping_client(Client3)),
+
+ Proc1 = get_client_proc(Client1, "1"),
+ Proc2 = get_client_proc(Client2, "2"),
+ Proc3 = get_client_proc(Client3, "3"),
+
+ ?assertNotEqual(Proc1, Proc2),
+ ?assertNotEqual(Proc2, Proc3),
+ ?assertNotEqual(Proc3, Proc1),
+
+ Client4 = spawn_client(),
+ ?assertEqual(timeout, ping_client(Client4)),
+
+ ?assertEqual(ok, stop_client(Client1)),
+ ?assertEqual(ok, ping_client(Client4)),
+
+ Proc4 = get_client_proc(Client4, "4"),
+
+ ?assertEqual(Proc1#proc.pid, Proc4#proc.pid),
+ ?assertNotEqual(Proc1#proc.client, Proc4#proc.client),
+
+ lists:map(fun(C) ->
+ ?assertEqual(ok, stop_client(C))
+ end, [Client2, Client3, Client4])
+ end).
+
+
+should_free_slot_on_proc_unexpected_exit() ->
+ ?_test(begin
+ Client1 = spawn_client(),
+ Client2 = spawn_client(),
+ Client3 = spawn_client(),
+
+ ?assertEqual(ok, ping_client(Client1)),
+ ?assertEqual(ok, ping_client(Client2)),
+ ?assertEqual(ok, ping_client(Client3)),
+
+ Proc1 = get_client_proc(Client1, "1"),
+ Proc2 = get_client_proc(Client2, "2"),
+ Proc3 = get_client_proc(Client3, "3"),
+
+ ?assertNotEqual(Proc1#proc.pid, Proc2#proc.pid),
+ ?assertNotEqual(Proc1#proc.client, Proc2#proc.client),
+ ?assertNotEqual(Proc2#proc.pid, Proc3#proc.pid),
+ ?assertNotEqual(Proc2#proc.client, Proc3#proc.client),
+ ?assertNotEqual(Proc3#proc.pid, Proc1#proc.pid),
+ ?assertNotEqual(Proc3#proc.client, Proc1#proc.client),
+
+ ?assertEqual(ok, kill_client(Client1)),
+
+ Client4 = spawn_client(),
+ ?assertEqual(ok, ping_client(Client4)),
+
+ Proc4 = get_client_proc(Client4, "4"),
+
+ ?assertEqual(Proc4#proc.pid, Proc1#proc.pid),
+ ?assertNotEqual(Proc4#proc.client, Proc1#proc.client),
+ ?assertNotEqual(Proc2#proc.pid, Proc4#proc.pid),
+ ?assertNotEqual(Proc2#proc.client, Proc4#proc.client),
+ ?assertNotEqual(Proc3#proc.pid, Proc4#proc.pid),
+ ?assertNotEqual(Proc3#proc.client, Proc4#proc.client),
+
+ lists:map(fun(C) ->
+ ?assertEqual(ok, stop_client(C))
+ end, [Client2, Client3, Client4])
+ end).
+
+
+should_reuse_known_proc() ->
+ ?_test(begin
+ Client1 = spawn_client(<<"ddoc1">>),
+ Client2 = spawn_client(<<"ddoc2">>),
+
+ ?assertEqual(ok, ping_client(Client1)),
+ ?assertEqual(ok, ping_client(Client2)),
+
+ Proc1 = get_client_proc(Client1, "1"),
+ Proc2 = get_client_proc(Client2, "2"),
+ ?assertNotEqual(Proc1#proc.pid, Proc2#proc.pid),
+
+ ?assertEqual(ok, stop_client(Client1)),
+ ?assertEqual(ok, stop_client(Client2)),
+ ?assert(is_process_alive(Proc1#proc.pid)),
+ ?assert(is_process_alive(Proc2#proc.pid)),
+
+ Client1Again = spawn_client(<<"ddoc1">>),
+ ?assertEqual(ok, ping_client(Client1Again)),
+ Proc1Again = get_client_proc(Client1Again, "1-again"),
+ ?assertEqual(Proc1#proc.pid, Proc1Again#proc.pid),
+ ?assertNotEqual(Proc1#proc.client, Proc1Again#proc.client),
+ ?assertEqual(ok, stop_client(Client1Again))
+ end).
+
+
+%should_process_waiting_queue_as_fifo() ->
+% ?_test(begin
+% Client1 = spawn_client(<<"ddoc1">>),
+% Client2 = spawn_client(<<"ddoc2">>),
+% Client3 = spawn_client(<<"ddoc3">>),
+% Client4 = spawn_client(<<"ddoc4">>),
+% timer:sleep(100),
+% Client5 = spawn_client(<<"ddoc5">>),
+%
+% ?assertEqual(ok, ping_client(Client1)),
+% ?assertEqual(ok, ping_client(Client2)),
+% ?assertEqual(ok, ping_client(Client3)),
+% ?assertEqual(timeout, ping_client(Client4)),
+% ?assertEqual(timeout, ping_client(Client5)),
+%
+% Proc1 = get_client_proc(Client1, "1"),
+% ?assertEqual(ok, stop_client(Client1)),
+% ?assertEqual(ok, ping_client(Client4)),
+% Proc4 = get_client_proc(Client4, "4"),
+%
+% ?assertNotEqual(Proc4#proc.client, Proc1#proc.client),
+% ?assertEqual(Proc1#proc.pid, Proc4#proc.pid),
+% ?assertEqual(timeout, ping_client(Client5)),
+%
+% ?assertEqual(ok, stop_client(Client2)),
+% ?assertEqual(ok, stop_client(Client3)),
+% ?assertEqual(ok, stop_client(Client4)),
+% ?assertEqual(ok, stop_client(Client5))
+% end).
+
+
+should_reduce_pool_on_idle_os_procs() ->
+ ?_test(begin
+ %% os_process_idle_limit is in sec
+ config:set("query_server_config",
+ "os_process_idle_limit", "1", false),
+ ok = confirm_config("os_process_idle_limit", "1"),
+
+ Client1 = spawn_client(<<"ddoc1">>),
+ Client2 = spawn_client(<<"ddoc2">>),
+ Client3 = spawn_client(<<"ddoc3">>),
+
+ ?assertEqual(ok, ping_client(Client1)),
+ ?assertEqual(ok, ping_client(Client2)),
+ ?assertEqual(ok, ping_client(Client3)),
+
+ ?assertEqual(3, couch_proc_manager:get_proc_count()),
+
+ ?assertEqual(ok, stop_client(Client1)),
+ ?assertEqual(ok, stop_client(Client2)),
+ ?assertEqual(ok, stop_client(Client3)),
+
+ timer:sleep(1200),
+ ?assertEqual(1, couch_proc_manager:get_proc_count())
+ end).
+
+
+setup_config() ->
+ config:set("native_query_servers", "enable_erlang_query_server", "true", false),
+ config:set("query_server_config", "os_process_limit", "3", false),
+ config:set("query_server_config", "os_process_soft_limit", "2", false),
+ ok = confirm_config("os_process_soft_limit", "2").
+
+confirm_config(Key, Value) ->
+ confirm_config(Key, Value, 0).
+
+confirm_config(Key, Value, Count) ->
+ case config:get("query_server_config", Key) of
+ Value ->
+ ok;
+ _ when Count > 10 ->
+ erlang:error({config_setup, [
+ {module, ?MODULE},
+ {line, ?LINE},
+ {value, timeout}
+ ]});
+ _ ->
+ %% we need to wait to let gen_server:cast finish
+ timer:sleep(10),
+ confirm_config(Key, Value, Count + 1)
+ end.
+
+spawn_client() ->
+ Parent = self(),
+ Ref = make_ref(),
+ Pid = spawn(fun() ->
+ Proc = couch_query_servers:get_os_process(<<"erlang">>),
+ loop(Parent, Ref, Proc)
+ end),
+ {Pid, Ref}.
+
+spawn_client(DDocId) ->
+ Parent = self(),
+ Ref = make_ref(),
+ Pid = spawn(fun() ->
+ DDocKey = {DDocId, <<"1-abcdefgh">>},
+ DDoc = #doc{body={[{<<"language">>, <<"erlang">>}]}},
+ Proc = couch_query_servers:get_ddoc_process(DDoc, DDocKey),
+ loop(Parent, Ref, Proc)
+ end),
+ {Pid, Ref}.
+
+ping_client({Pid, Ref}) ->
+ Pid ! ping,
+ receive
+ {pong, Ref} ->
+ ok
+ after ?TIMEOUT ->
+ timeout
+ end.
+
+get_client_proc({Pid, Ref}, ClientName) ->
+ Pid ! get_proc,
+ receive
+ {proc, Ref, Proc} -> Proc
+ after ?TIMEOUT ->
+ erlang:error({assertion_failed,
+ [{module, ?MODULE},
+ {line, ?LINE},
+ {reason, "Timeout getting client "
+ ++ ClientName ++ " proc"}]})
+ end.
+
+stop_client({Pid, Ref}) ->
+ Pid ! stop,
+ receive
+ {stop, Ref} ->
+ ok
+ after ?TIMEOUT ->
+ timeout
+ end.
+
+kill_client({Pid, Ref}) ->
+ Pid ! die,
+ receive
+ {die, Ref} ->
+ ok
+ after ?TIMEOUT ->
+ timeout
+ end.
+
+loop(Parent, Ref, Proc) ->
+ receive
+ ping ->
+ Parent ! {pong, Ref},
+ loop(Parent, Ref, Proc);
+ get_proc ->
+ Parent ! {proc, Ref, Proc},
+ loop(Parent, Ref, Proc);
+ stop ->
+ couch_query_servers:ret_os_process(Proc),
+ Parent ! {stop, Ref};
+ die ->
+ Parent ! {die, Ref},
+ exit(some_error)
+ end.