From f77a170a60af082234cba797b4423795d07e0b81 Mon Sep 17 00:00:00 2001 From: Nick Vatamaniuc Date: Tue, 29 Oct 2024 20:50:53 -0400 Subject: [PATCH] Allow online js_engine reconfiguration This should make it possible to toggle between engines without having to restart the node. We already had a config listener so make sure it also sets up the js_engine bits, as well. Add a test to demonstrate it works both when setting a non-default value and reverting back to the default value. --- src/couch/src/couch_proc_manager.erl | 22 ++++++-- .../test/couch_quickjs_tests.erl | 56 ++++++++++++++++++- 2 files changed, 70 insertions(+), 8 deletions(-) diff --git a/src/couch/src/couch_proc_manager.erl b/src/couch/src/couch_proc_manager.erl index 885a438f73c..73dbb17b16a 100644 --- a/src/couch/src/couch_proc_manager.erl +++ b/src/couch/src/couch_proc_manager.erl @@ -139,11 +139,7 @@ init([]) -> % Lang -> number of procs spawn for that lang ets:new(?COUNTERS, [named_table]), - ets:insert(?SERVERS, get_servers_from_env("COUCHDB_QUERY_SERVER_")), - ets:insert(?SERVERS, get_servers_from_env("COUCHDB_NATIVE_QUERY_SERVER_")), - ets:insert(?SERVERS, [{"QUERY", {mango_native_proc, start_link, []}}]), - maybe_configure_erlang_native_servers(), - configure_js_engine(couch_server:get_js_engine()), + ok = configure_language_servers(), {ok, #state{ config = get_proc_config(), @@ -221,7 +217,7 @@ handle_cast(reload_config, State) -> hard_limit = get_hard_limit(), soft_limit = get_soft_limit() }, - maybe_configure_erlang_native_servers(), + ok = configure_language_servers(), lists:foreach( fun({Lang, _}) -> ok = flush_waiters(NewState, Lang) @@ -290,6 +286,9 @@ handle_config_change("native_query_servers", _, _, _, _) -> handle_config_change("query_server_config", _, _, _, _) -> gen_server:cast(?MODULE, reload_config), {ok, undefined}; +handle_config_change("couchdb", "js_engine", _, _, _) -> + gen_server:cast(?MODULE, reload_config), + {ok, undefined}; handle_config_change(_, _, _, _, _) -> {ok, undefined}. @@ -512,6 +511,17 @@ get_servers_from_env(Spec) -> os:getenv() ). +configure_language_servers() -> + ets:delete_all_objects(?SERVERS), + % NOTE: process environment variables cannot be altered except by + % debugging the process or via os:putenv/2 calls. + ets:insert(?SERVERS, get_servers_from_env("COUCHDB_QUERY_SERVER_")), + ets:insert(?SERVERS, get_servers_from_env("COUCHDB_NATIVE_QUERY_SERVER_")), + ets:insert(?SERVERS, [{"QUERY", {mango_native_proc, start_link, []}}]), + maybe_configure_erlang_native_servers(), + configure_js_engine(couch_server:get_js_engine()), + ok. + get_query_server(LangStr) -> case ets:lookup(?SERVERS, string:to_upper(LangStr)) of [{_, Command}] -> Command; diff --git a/src/couch_quickjs/test/couch_quickjs_tests.erl b/src/couch_quickjs/test/couch_quickjs_tests.erl index b1089e3e579..200586c1082 100644 --- a/src/couch_quickjs/test/couch_quickjs_tests.erl +++ b/src/couch_quickjs/test/couch_quickjs_tests.erl @@ -18,7 +18,9 @@ setup() -> test_util:start_couch(). teardown(Ctx) -> - config:delete("quickjs", "memory_limit_bytes", _Persist = false), + Persist = false, + config:delete("quickjs", "memory_limit_bytes", Persist), + config:delete("couchdb", "js_engine", Persist), test_util:stop_couch(Ctx). quickjs_test_() -> @@ -30,7 +32,8 @@ quickjs_test_() -> ?TDEF_FE(t_get_mainjs_cmd), ?TDEF_FE(t_get_coffee_cmd), ?TDEF_FE(t_can_configure_memory_limit), - ?TDEF_FE(t_bad_memory_limit) + ?TDEF_FE(t_bad_memory_limit), + ?TDEF_FE(t_couch_jsengine_config_triggers_proc_server_reload) ] }. @@ -60,9 +63,58 @@ t_bad_memory_limit(_) -> ?assertEqual(ExpectCmd, string:find(Cmd, ExpectCmd)), ?assertEqual(1, os_cmd(Cmd ++ " -V")). +t_couch_jsengine_config_triggers_proc_server_reload(_) -> + case couch_server:with_spidermonkey() of + false -> + % Spidermonkey is not in the build at all, skip the test + ok; + true -> + OldVal = get_proc_manager_default_js(), + % In the test, set the engine to whatever is not the default + Toggle = + case couch_server:get_js_engine() of + <<"quickjs">> -> "spidermonkey"; + <<"spidermonkey">> -> "quickjs" + end, + + config:set("couchdb", "js_engine", Toggle, false), + % couch_server:get_js_engine/0 should be visible immediately + ?assertEqual(list_to_binary(Toggle), couch_server:get_js_engine()), + + wait_until_proc_manager_updates(OldVal), + ?assertNotEqual(OldVal, get_proc_manager_default_js()), + NewVal = get_proc_manager_default_js(), + + % Toggle back to the original default (test config:delete/3) + config:delete("couchdb", "js_engine", false), + wait_until_proc_manager_updates(NewVal), + ?assertNotEqual(NewVal, get_proc_manager_default_js()), + % We should be back to the original default + ?assertEqual(OldVal, get_proc_manager_default_js()) + end. + os_cmd(Cmd) -> Opts = [stream, {line, 4096}, binary, exit_status, hide], Port = open_port({spawn, Cmd}, Opts), receive {Port, {exit_status, Status}} -> Status end. + +wait_until_proc_manager_updates(OldVal) -> + WaitFun = fun() -> + case get_proc_manager_default_js() of + OldVal -> wait; + not_found -> wait; + _ -> ok + end + end, + case test_util:wait(WaitFun, 4500) of + timeout -> error(timeout); + _ -> ok + end. + +get_proc_manager_default_js() -> + case ets:lookup(couch_proc_manager_servers, "JAVASCRIPT") of + [{"JAVASCRIPT", Val}] -> Val; + _ -> not_found + end.