You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by va...@apache.org on 2024/06/22 01:03:55 UTC
(couchdb) branch quickjs-fixes created (now 649cd8a56)
This is an automated email from the ASF dual-hosted git repository.
vatamane pushed a change to branch quickjs-fixes
in repository https://gitbox.apache.org/repos/asf/couchdb.git
at 649cd8a56 Improve QuickJS scanner plugin
This branch includes the following new commits:
new 8628e03b5 Make QuickJS memory limits match Spidermonkey
new aed4e2aa0 Make QuickJS dispatch respond with an error instead of throwing a null
new 649cd8a56 Improve QuickJS scanner plugin
The 3 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) 03/03: Improve QuickJS scanner plugin
Posted by va...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
vatamane pushed a commit to branch quickjs-fixes
in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit 649cd8a5604fda6ac5be4628f9e117427426be80
Author: Nick Vatamaniuc <va...@gmail.com>
AuthorDate: Fri Jun 21 20:45:20 2024 -0400
Improve QuickJS scanner plugin
Some improvements I noticed we could make after scanning a few clusters.
* Be more resilient to errors, so we log the errors but do not get do not get
stuck forever retrying to rescan the same db/ddoc/doc.
* Reduce the memory and doc batch size limits. Previous size and memory limit
were taken from the mrview index settings, but we're also dealing with VDUs,
filters, and other things, so to keep the engines from crashing or consuming
too many resources opt to reduce the limits a bit at the expense of possibly
running longer.
* Previously, if a view check crashed the JS process, and it died, then any
filter or VDU checks for the same DB will fail because the new JS processes
won't have the "taught" design documents. To fix this, if we notice the SM or
QJS process is dead, we respawn it, and then also load all the design docs.
* Since we're not building an actual view btree, don't set a reduce limit. We
send potentially a lot more data to the reduce functions, and we'd have a
higher chance of triggering the reduce limit exception. So then we just end up
showing a discrepancy in stack traces and reduce limit error format, instead
of validationg the actual output of the reduce functions.
* Since QuickJS is the new engine, and is mostly likely to show issues or
crashes, always try to use the Spidermonkey process first instead of the
QuickJS one. Otherwise if SM crashes we may not get a good signal if there is
some discrepancy between engines, or just a bad JS code in general that throws
errors anyway.
* Add a few more tests for some large reduce values and some odd formatting
around functions.
---
.../src/couch_quickjs_scanner_plugin.erl | 175 ++++++++++++++-------
.../test/couch_quickjs_scanner_plugin_tests.erl | 47 +++++-
src/couch_scanner/include/couch_scanner_plugin.hrl | 2 +
3 files changed, 164 insertions(+), 60 deletions(-)
diff --git a/src/couch_quickjs/src/couch_quickjs_scanner_plugin.erl b/src/couch_quickjs/src/couch_quickjs_scanner_plugin.erl
index c68313d43..8d4d18fca 100644
--- a/src/couch_quickjs/src/couch_quickjs_scanner_plugin.erl
+++ b/src/couch_quickjs/src/couch_quickjs_scanner_plugin.erl
@@ -43,9 +43,8 @@
max_shards = 4,
max_docs = 1000,
max_step = 1000,
- % These match defaults in couch_mrview_updater
- max_batch_items = 100,
- max_batch_size = 16777216
+ max_batch_items = 50,
+ max_batch_size = 8388608
}).
% DDoc fields
@@ -131,37 +130,17 @@ doc_id(#st{doc_cnt = C} = St, _DocId, _Db) ->
{ok, St#st{doc_cnt = C + 1}}.
doc(#st{} = St, Db, #doc{id = DocId} = Doc) ->
- #st{
- sid = SId,
- ddocs = DDocs,
- docs = Docs,
- docs_size = DocsSize,
- max_batch_items = MaxItems,
- max_batch_size = MaxSize
- } = St,
+ #st{sid = SId} = St,
JsonDoc = couch_query_servers:json_doc(Doc),
- DDocFun = fun(DDocId, #{} = DDoc) ->
- try
- Filters = maps:get(?FILTERS, DDoc, undefined),
- filter_doc_validate(St, DDocId, Filters, JsonDoc),
- VDU = maps:get(?VDU, DDoc, undefined),
- vdu_doc_validate(St, DDocId, VDU, JsonDoc)
- catch
- throw:{validate, Error} ->
- Meta = #{sid => SId, db => Db, ddoc => DDocId, doc => DocId},
- ?WARN("doc validation failed ~p", [Error], Meta)
- end
- end,
- maps:foreach(DDocFun, DDocs),
- DocsSize1 = DocsSize + ?term_size(JsonDoc),
- case DocsSize1 < MaxSize andalso length(Docs) < MaxItems of
- true ->
- St1 = St#st{docs = [JsonDoc | Docs], docs_size = DocsSize1},
- {ok, St1};
- false ->
- {_Db, St1} = maps:fold(fun views_validate/3, {Db, St}, DDocs),
- St2 = St1#st{docs = [], docs_size = 0},
- {ok, St2}
+ try
+ St1 = maybe_reset_and_teach_ddocs(St),
+ process_doc_filter_and_vdu(St1, Db, DocId, JsonDoc),
+ process_doc_views(St1, Db, JsonDoc)
+ catch
+ Tag:Err:Stack ->
+ Meta = #{sid => SId, db => Db, doc => DocId},
+ ?ERR("doc validation exception ~p:~p:~p", [Tag, Err, Stack], Meta),
+ {ok, St}
end.
db_closing(#st{docs = []} = St, _Db) ->
@@ -197,23 +176,67 @@ process_ddoc(#st{} = St, DbName, #doc{} = DDoc0) ->
views_load(St1, maps:get(?VIEWS, DDoc, undefined)),
filters_load(St1, maps:get(?FILTERS, DDoc, undefined)),
vdu_load(St1, maps:get(?VDU, DDoc, undefined)),
- reset_procs(St1),
- teach_ddoc_validate(St1, DDocId, DDoc),
- St1#st{ddocs = DDocs#{DDocId => DDoc}}
+ St2 = start_or_reset_procs(St1),
+ teach_ddoc_validate(St2, DDocId, DDoc),
+ St2#st{ddocs = DDocs#{DDocId => DDoc}}
catch
throw:{validate, Error} ->
Meta = #{sid => SId, db => DbName, ddoc => DDocId},
?WARN("ddoc validation failed ~p", [Error], Meta),
- St1
+ St1;
+ Tag:Err:Stack ->
+ Meta = #{sid => SId, db => DbName, ddoc => DDocId},
+ ?ERR("ddoc validation exception ~p:~p:~p", [Tag, Err, Stack], Meta)
end;
false ->
St
end.
+process_doc_filter_and_vdu(#st{} = St, Db, DocId, JsonDoc) ->
+ #st{sid = SId, ddocs = DDocs} = St,
+ DDocFun = fun(DDocId, #{} = DDoc) ->
+ try
+ Filters = maps:get(?FILTERS, DDoc, undefined),
+ filter_doc_validate(St, DDocId, Filters, JsonDoc),
+ VDU = maps:get(?VDU, DDoc, undefined),
+ vdu_doc_validate(St, DDocId, VDU, JsonDoc)
+ catch
+ throw:{validate, Error} ->
+ Meta = #{sid => SId, db => Db, ddoc => DDocId, doc => DocId},
+ ?WARN("doc validation failed ~p", [Error], Meta)
+ end
+ end,
+ try
+ maps:foreach(DDocFun, DDocs)
+ catch
+ Tag:Err:Stack ->
+ Meta = #{sid => SId, db => Db, doc => DocId},
+ ?ERR("Exception when validating doc ~p:~p:~p", [Tag, Err, Stack], Meta)
+ end.
+
+process_doc_views(#st{} = St, Db, JsonDoc) ->
+ #st{
+ ddocs = DDocs,
+ docs = Docs,
+ docs_size = DocsSize,
+ max_batch_items = MaxItems,
+ max_batch_size = MaxSize
+ } = St,
+ DocsSize1 = DocsSize + ?term_size(JsonDoc),
+ case DocsSize1 < MaxSize andalso length(Docs) < MaxItems of
+ true ->
+ St1 = St#st{docs = [JsonDoc | Docs], docs_size = DocsSize1},
+ {ok, St1};
+ false ->
+ {_Db, St1} = maps:fold(fun views_validate/3, {Db, St}, DDocs),
+ St2 = St1#st{docs = [], docs_size = 0},
+ {ok, St2}
+ end.
+
views_validate(DDocId, #{?VIEWS := Views} = DDoc, {Db, #st{} = St0}) when
map_size(Views) > 0
->
- St = reset_procs(St0),
+ St = start_or_reset_procs(St0),
#st{sid = SId, docs = Docs} = St,
try
lib_load(St, maps:get(?LIB, DDoc, undefined)),
@@ -226,6 +249,10 @@ views_validate(DDocId, #{?VIEWS := Views} = DDoc, {Db, #st{} = St0}) when
throw:{validate, Error} ->
Meta = #{sid => SId, db => Db, ddoc => DDocId},
?WARN("view validation failed ~p", [Error], Meta),
+ {Db, St};
+ Tag:Err:Stack ->
+ Meta = #{sid => SId, db => Db, ddoc => DDocId},
+ ?ERR("view validation exception ~p:~p:~p", [Tag, Err, Stack], Meta),
{Db, St}
end;
views_validate(_DDocId, #{} = _DDoc, {Db, #st{} = St}) ->
@@ -235,8 +262,8 @@ views_validate(_DDocId, #{} = _DDoc, {Db, #st{} = St}) ->
mapred_fold({Props = [_ | _]} = Doc, {ViewList = [_ | _], #st{} = St}) ->
#st{qjs_proc = Qjs, sm_proc = Sm} = St,
DocId = couch_util:get_value(<<"_id">>, Props),
- QjsMapRes = map_doc(Qjs, Doc),
SmMapRes = map_doc(Sm, Doc),
+ QjsMapRes = map_doc(Qjs, Doc),
case QjsMapRes == SmMapRes of
true -> ok;
false -> throw({validate, {map_doc, DocId, QjsMapRes, SmMapRes}})
@@ -252,8 +279,8 @@ mapred_fold({Props = [_ | _]} = Doc, {ViewList = [_ | _], #st{} = St}) ->
{ViewList, St}.
reset_per_db_state(#st{qjs_proc = QjsProc, sm_proc = SmProc} = St) ->
- proc_stop(QjsProc),
proc_stop(SmProc),
+ proc_stop(QjsProc),
St#st{
ddocs = #{},
docs = [],
@@ -263,8 +290,8 @@ reset_per_db_state(#st{qjs_proc = QjsProc, sm_proc = SmProc} = St) ->
}.
start_or_reset_procs(#st{} = St) ->
- St1 = start_or_reset_qjs_proc(St),
- start_or_reset_sm_proc(St1).
+ St1 = start_or_reset_sm_proc(St),
+ start_or_reset_qjs_proc(St1).
start_or_reset_qjs_proc(#st{qjs_proc = undefined} = St) ->
Cmd = couch_quickjs:mainjs_cmd(),
@@ -277,6 +304,9 @@ start_or_reset_qjs_proc(#st{qjs_proc = #proc{} = Proc} = St) ->
true = proc_reset(Proc),
St
catch
+ exit:{noproc, _} ->
+ proc_stop(Proc),
+ start_or_reset_qjs_proc(St#st{qjs_proc = undefined});
Tag:Err ->
?WARN("failed to reset QuickJS proc ~p:~p", [Tag, Err]),
proc_stop(Proc),
@@ -294,6 +324,9 @@ start_or_reset_sm_proc(#st{sm_proc = #proc{} = Proc} = St) ->
true = proc_reset(Proc),
St
catch
+ exit:{noproc, _} ->
+ proc_stop(Proc),
+ start_or_reset_sm_proc(St#st{sm_proc = undefined});
Tag:Err ->
?WARN("failed to reset Spidermonkey proc ~p:~p", [Tag, Err]),
proc_stop(Proc),
@@ -303,8 +336,8 @@ start_or_reset_sm_proc(#st{sm_proc = #proc{} = Proc} = St) ->
lib_load(#st{}, undefined) ->
ok;
lib_load(#st{qjs_proc = Qjs, sm_proc = Sm}, #{} = Lib) ->
- QjsRes = add_lib(Qjs, Lib),
SmRes = add_lib(Sm, Lib),
+ QjsRes = add_lib(Qjs, Lib),
case QjsRes == SmRes of
true -> ok;
false -> throw({validate, {add_lib, QjsRes, SmRes}})
@@ -325,8 +358,8 @@ view_load(#st{} = St, Name, View) ->
add_fun_load(#st{}, _, undefined) ->
ok;
add_fun_load(#st{qjs_proc = Qjs, sm_proc = Sm}, Name, Src) ->
- QjsRes = add_fun(Qjs, Src),
SmRes = add_fun(Sm, Src),
+ QjsRes = add_fun(Qjs, Src),
case QjsRes == SmRes of
true -> ok;
false -> throw({validate, {add_fun, Name, QjsRes, SmRes}})
@@ -343,16 +376,16 @@ reduce_filter_map({{_Name, #{}}, _KVs}) ->
view_reduce_validate(#st{} = St, ReduceKVs) ->
#st{qjs_proc = Qjs, sm_proc = Sm} = St,
RedFun = fun({Name, RedSrc, KVs}) ->
- QjsRedRes = reduce(Qjs, RedSrc, KVs),
SmRedRes = reduce(Sm, RedSrc, KVs),
+ QjsRedRes = reduce(Qjs, RedSrc, KVs),
case QjsRedRes == SmRedRes of
true -> ok;
false -> throw({validate, {reduce, Name, QjsRedRes, SmRedRes}})
end,
case QjsRedRes of
[true, [_ | _] = RedVals] ->
- QjsRRedRes = rereduce(Qjs, RedSrc, RedVals),
SmRRedRes = rereduce(Sm, RedSrc, RedVals),
+ QjsRRedRes = rereduce(Qjs, RedSrc, RedVals),
case QjsRRedRes == SmRRedRes of
true -> ok;
false -> throw({validate, {rereduce, Name, QjsRRedRes, SmRRedRes}})
@@ -370,8 +403,8 @@ filters_load(#st{} = St, #{} = Filters) ->
maps:foreach(Fun, Filters).
filter_load(#st{qjs_proc = Qjs, sm_proc = Sm}, Name, Filter) ->
- QjsRes = add_fun(Qjs, Filter),
SmRes = add_fun(Sm, Filter),
+ QjsRes = add_fun(Qjs, Filter),
case QjsRes == SmRes of
true -> ok;
false -> throw({validate, {filter, Name, QjsRes, SmRes}})
@@ -382,8 +415,8 @@ filter_doc_validate(#st{}, _, undefined, _) ->
filter_doc_validate(#st{} = St, DDocId, #{} = Filters, Doc) ->
#st{qjs_proc = Qjs, sm_proc = Sm} = St,
Fun = fun(FName, _) ->
- QjsRes = filter_doc(Qjs, DDocId, FName, Doc),
SmRes = filter_doc(Sm, DDocId, FName, Doc),
+ QjsRes = filter_doc(Qjs, DDocId, FName, Doc),
case QjsRes == SmRes of
true -> ok;
false -> throw({validate, {filter_doc, FName, QjsRes, SmRes}})
@@ -394,8 +427,8 @@ filter_doc_validate(#st{} = St, DDocId, #{} = Filters, Doc) ->
vdu_load(#st{}, undefined) ->
ok;
vdu_load(#st{qjs_proc = Qjs, sm_proc = Sm}, VDU) ->
- QjsRes = add_fun(Qjs, VDU),
SmRes = add_fun(Sm, VDU),
+ QjsRes = add_fun(Qjs, VDU),
case QjsRes == SmRes of
true -> ok;
false -> throw({validate, {vdu, QjsRes, SmRes}})
@@ -406,25 +439,46 @@ vdu_doc_validate(#st{}, _DDocId, undefined, _Doc) ->
ok;
vdu_doc_validate(#st{} = St, DDocId, _VDU, Doc) ->
#st{qjs_proc = Qjs, sm_proc = Sm} = St,
- QjsRes = vdu_doc(Qjs, DDocId, Doc),
SmRes = vdu_doc(Sm, DDocId, Doc),
+ QjsRes = vdu_doc(Qjs, DDocId, Doc),
case QjsRes == SmRes of
true -> ok;
false -> throw({validate, {vdu_doc, QjsRes, SmRes}})
end.
teach_ddoc_validate(#st{qjs_proc = Qjs, sm_proc = Sm}, DDocId, DDoc) ->
- QjsRes = teach_ddoc(Qjs, DDocId, DDoc),
SmRes = teach_ddoc(Sm, DDocId, DDoc),
+ QjsRes = teach_ddoc(Qjs, DDocId, DDoc),
case QjsRes == SmRes of
true -> ok;
false -> throw({validate, {teach_ddoc, DDocId, QjsRes, SmRes}})
end.
-reset_procs(#st{qjs_proc = #proc{} = QjsProc, sm_proc = #proc{} = SmProc} = St) ->
- true = proc_reset(QjsProc),
- true = proc_reset(SmProc),
- St.
+% During view validation if an JS process crashes and exists the next filter
+% and vdu validation for the same db will fail as the "taught" desing document
+% will not be in loaded in the JS process ddoc cache. To make sure we continue
+% validating, try restart the process and reload all the ddocs.
+%
+maybe_reset_and_teach_ddocs(#st{ddocs = DDocs} = St) ->
+ St1 =
+ case is_proc_alive(St#st.sm_proc) of
+ true ->
+ St;
+ false ->
+ StSm = start_or_reset_sm_proc(St),
+ FunSm = fun(DDocId, DDoc) -> teach_ddoc(StSm#st.sm_proc, DDocId, DDoc) end,
+ maps:foreach(FunSm, DDocs),
+ StSm
+ end,
+ case is_proc_alive(St#st.qjs_proc) of
+ true ->
+ St1;
+ false ->
+ StQjs = start_or_reset_qjs_proc(St1),
+ FunQjs = fun(DDocId, DDoc) -> teach_ddoc(StQjs#st.qjs_proc, DDocId, DDoc) end,
+ maps:foreach(FunQjs, DDocs),
+ StQjs
+ end.
% Proc commands
@@ -449,17 +503,19 @@ add_fun(#proc{} = Proc, <<_/binary>> = FunSrc) ->
prompt(Proc, [<<"add_fun">>, FunSrc]).
filter_doc(#proc{} = Proc, DDocId, FName, {[_ | _]} = Doc) ->
- prompt(Proc, [<<"ddoc">>, DDocId, [<<"filters">>, FName], [[Doc], #{}]]).
+ % Add a mock request object so param access doesn't throw a TypeError
+ MockReq = #{<<"query">> => #{}},
+ prompt(Proc, [<<"ddoc">>, DDocId, [<<"filters">>, FName], [[Doc], MockReq]]).
vdu_doc(#proc{} = Proc, DDocId, {[_ | _]} = Doc) ->
prompt(Proc, [<<"ddoc">>, DDocId, [<<"validate_doc_update">>], [Doc, Doc]]).
teach_ddoc(#proc{} = Proc, DDocId, DDoc) ->
- true = prompt(Proc, [<<"ddoc">>, <<"new">>, DDocId, DDoc]).
+ prompt(Proc, [<<"ddoc">>, <<"new">>, DDocId, DDoc]).
proc_reset(#proc{} = Proc) ->
Timeout = config:get_integer("couchdb", "os_process_timeout", 5000),
- Cfg = [{<<"reduce_limit">>, true}, {<<"timeout">>, Timeout}],
+ Cfg = [{<<"timeout">>, Timeout}],
Result = prompt(Proc, [<<"reset">>, {Cfg}]),
proc_set_timeout(Proc, Timeout),
Result.
@@ -468,6 +524,9 @@ proc_reset(#proc{} = Proc) ->
% Proc utils
+is_proc_alive(#proc{pid = Pid}) ->
+ is_process_alive(Pid).
+
proc(Pid) ->
#proc{
pid = Pid,
diff --git a/src/couch_quickjs/test/couch_quickjs_scanner_plugin_tests.erl b/src/couch_quickjs/test/couch_quickjs_scanner_plugin_tests.erl
index de6bfe806..9f3489180 100644
--- a/src/couch_quickjs/test/couch_quickjs_scanner_plugin_tests.erl
+++ b/src/couch_quickjs/test/couch_quickjs_scanner_plugin_tests.erl
@@ -53,6 +53,7 @@ setup() ->
ok = add_doc(DbName, ?DOC4, #{a => w}),
ok = add_doc(DbName, ?DOC5, #{a => u}),
config:set(atom_to_list(?PLUGIN), "max_batch_items", "1", false),
+ reset_stats(),
{Ctx, DbName}.
teardown({Ctx, DbName}) ->
@@ -260,6 +261,22 @@ t_empty_ddoc({_, DbName}) ->
ok
end.
+reset_stats() ->
+ Counters = [
+ [couchdb, query_server, process_error_exits],
+ [couchdb, query_server, process_errors],
+ [couchdb, query_server, process_exits]
+ ],
+ [reset_counter(C) || C <- Counters].
+
+reset_counter(Counter) ->
+ case couch_stats:sample(Counter) of
+ 0 ->
+ ok;
+ N when is_integer(N), N > 0 ->
+ couch_stats:decrement_counter(Counter, N)
+ end.
+
config_delete_section(Section) ->
[config:delete(K, V, false) || {K, V} <- config:get(Section)].
@@ -306,6 +323,12 @@ ddoc_filter(Doc) ->
" return true;\n"
" }\n"
"}"
+ >>,
+ f_use_req => <<
+ "function (doc, req) {\n"
+ " if (doc.a == req.query.foo) {return true;} \n"
+ " return false; \n"
+ "}\n"
>>
}
}.
@@ -313,7 +336,7 @@ ddoc_filter(Doc) ->
ddoc_view(Doc) ->
Doc#{
views => #{
- v => #{
+ v1 => #{
map => <<
"function(doc) {\n"
" if(doc.a == 'x') {\n"
@@ -342,6 +365,26 @@ ddoc_view(Doc) ->
" }\n"
"}"
>>
- }
+ },
+ v_large_reduce => #{
+ map => <<
+ "function(doc) {\n"
+ " if(doc.a == 'x') {\n"
+ " r = doc.a.search(/(x+)/); emit(r, RegExp.$1)\n"
+ " } else {\n"
+ " for(i=0;i<10000;i++) {emit(doc.a, doc.a)};\n"
+ " }\n"
+ "}"
+ >>,
+ reduce => <<
+ "function(ks, vs, rereduce) {\n"
+ " return {'ks1': ks, 'ks2': k2, 'vs1': v2, 'v2':v2};\n"
+ "}"
+ >>
+ },
+ v_expr_fun1 => #{map => <<"(function(doc) {emit(1,2)})\n">>},
+ v_expr_fun2 => #{map => <<"(function(doc) {emit(3,4)});">>},
+ v_expr_fun3 => #{map => <<"y=9;\n(function(doc) {emit(5,y)})">>},
+ v_expr_fun4 => #{map => <<"x=10; function(doc) {emit(x,6)}\n">>}
}
}.
diff --git a/src/couch_scanner/include/couch_scanner_plugin.hrl b/src/couch_scanner/include/couch_scanner_plugin.hrl
index 76d7383d3..c2d0cc886 100644
--- a/src/couch_scanner/include/couch_scanner_plugin.hrl
+++ b/src/couch_scanner/include/couch_scanner_plugin.hrl
@@ -20,5 +20,7 @@
-define(INFO(), ?LOG(info, "", [], #{})).
-define(WARN(FMT, ARGS), ?LOG(warning, FMT, ARGS, #{})).
-define(WARN(FMT, ARGS, META), ?LOG(warning, FMT, ARGS, META)).
+-define(ERR(FMT, ARGS), ?LOG(error, FMT, ARGS, #{})).
+-define(ERR(FMT, ARGS, META), ?LOG(error, FMT, ARGS, META)).
-define(DEBUG(FMT, ARGS), ?LOG(debug, FMT, ARGS, #{fn => ?FUNCTION_NAME})).
-define(DEBUG(FMT, ARGS, META), ?LOG(debug, FMT, ARGS, maps:merge(META, #{fn => ?FUNCTION_NAME}))).
(couchdb) 01/03: Make QuickJS memory limits match Spidermonkey
Posted by va...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
vatamane pushed a commit to branch quickjs-fixes
in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit 8628e03b54b4a6e30ce3923b9fe2a5aa04521f8a
Author: Nick Vatamaniuc <va...@gmail.com>
AuthorDate: Fri Jun 21 20:40:37 2024 -0400
Make QuickJS memory limits match Spidermonkey
Spidermonkey only sets the stack limit size, so do the same for QuickJS.
---
src/couch_quickjs/c_src/couchjs.c | 2 --
1 file changed, 2 deletions(-)
diff --git a/src/couch_quickjs/c_src/couchjs.c b/src/couch_quickjs/c_src/couchjs.c
index 434d264e7..7958acda5 100644
--- a/src/couch_quickjs/c_src/couchjs.c
+++ b/src/couch_quickjs/c_src/couchjs.c
@@ -373,7 +373,6 @@ static JSContext* new_cx(const couch_args* args) {
BAIL("Could not create JSRuntime");
}
- JS_SetMemoryLimit(rt, args->stack_size);
JS_SetMaxStackSize(rt, args->stack_size);
cx = JS_NewContextRaw(rt);
@@ -501,4 +500,3 @@ int main(int argc, const char* argv[])
return EXIT_SUCCESS;
}
-
(couchdb) 02/03: Make QuickJS dispatch respond with an error instead of throwing a null
Posted by va...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
vatamane pushed a commit to branch quickjs-fixes
in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit aed4e2aa030326bdefff33ec30a6a2f5feef2ffd
Author: Nick Vatamaniuc <va...@gmail.com>
AuthorDate: Fri Jun 21 20:42:01 2024 -0400
Make QuickJS dispatch respond with an error instead of throwing a null
For some internal errors, especially when bumping into memory limits, a null
exception is thrown, instead of rethrowing try to behave like the SM loop.js
and other error handling by returning an error line and then indicating we
shouldn't continue. This way process crashes will be reserved for segfaults and
such.
---
share/server/dispatch-quickjs.js | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/share/server/dispatch-quickjs.js b/share/server/dispatch-quickjs.js
index fc159f4c9..78ece8b59 100644
--- a/share/server/dispatch-quickjs.js
+++ b/share/server/dispatch-quickjs.js
@@ -121,8 +121,9 @@ var DDoc = (function() {
function handleError(e) {
if (e === null) {
// internal error, another possibility when out of memory
- // nothing to do except rethrow and let main.c catch it and exit(1)
- throw(null);
+ // we tell the client it was a fatal error by dying
+ respond(["error", "internal_error", null]);
+ return false;
}
const type = e[0];
if (type == "fatal") {