diff options
author | Jan Lehnardt <jan@apache.org> | 2022-12-13 14:54:36 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-12-13 14:54:36 +0100 |
commit | 99a8f666c8b9d1f5a6ebbb09c83e59e1916c0205 (patch) | |
tree | fe4595e6b1bc63bbc9c979013968059d0a7b4bda | |
parent | 641586116fc6f78228f203c04e8eca9660e2027f (diff) | |
download | couchdb-99a8f666c8b9d1f5a6ebbb09c83e59e1916c0205.tar.gz |
fix(3517): super-simplistic fix to avoid costly AST transforms when t… (#4292)
* fix(3517): super-simplistic fix to avoid costly AST transforms when they are not needed
Co-authored-by: Ronny Berndt <ronny@apache.org>
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | Makefile.win | 2 | ||||
-rw-r--r-- | rel/reltool.config | 1 | ||||
-rw-r--r-- | share/server/60/rewrite_fun.js | 5 | ||||
-rw-r--r-- | share/server/60/rewrite_fun_ast_bypass.js | 61 | ||||
-rw-r--r-- | src/docs/src/config/query-servers.rst | 13 | ||||
-rw-r--r-- | support/build_js.escript | 15 |
8 files changed, 92 insertions, 8 deletions
diff --git a/.gitignore b/.gitignore index 816ece6d4..7c2b9f189 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,7 @@ rel/dev* rel/tmpdata share/server/main-coffee.js share/server/main.js +share/server/main-ast-bypass.js share/www src/bear/ src/certifi/ @@ -442,7 +442,7 @@ clean: @rm -rf src/*/.rebar @rm -rf src/*/priv/*.so @rm -rf src/couch/priv/{couchspawnkillable,couchjs} - @rm -rf share/server/main.js share/server/main-coffee.js + @rm -rf share/server/main.js share/server/main-ast-bypass.js share/server/main-coffee.js @rm -rf tmp dev/data dev/lib dev/logs @rm -rf src/mango/.venv @rm -f src/couch/priv/couchspawnkillable diff --git a/Makefile.win b/Makefile.win index 5bbfeead9..8a8129ff8 100644 --- a/Makefile.win +++ b/Makefile.win @@ -374,7 +374,7 @@ clean: -@rmdir /s/q src\*\.rebar -@del /f/q/s src\*.dll -@del /f/q src\couch\priv\*.exe - -@del /f/q share\server\main.js share\server\main-coffee.js + -@del /f/q share\server\main.js share\server\main-ast-bypass.js share\server\main-coffee.js -@rmdir /s/q tmp -@rmdir /s/q dev\data -@rmdir /s/q dev\lib diff --git a/rel/reltool.config b/rel/reltool.config index ab26fb2ed..f7c9df14b 100644 --- a/rel/reltool.config +++ b/rel/reltool.config @@ -141,6 +141,7 @@ {copy, "overlay/etc"}, {copy, "../src/couch/priv/couchjs", "bin/couchjs"}, {copy, "../share/server/main.js", "share/server/main.js"}, + {copy, "../share/server/main-ast-bypass.js", "share/server/main-ast-bypass.js"}, {copy, "../share/server/main-coffee.js", "share/server/main-coffee.js"}, {copy, "../src/weatherreport/weatherreport", "bin/weatherreport"}, {copy, "files/sys.config", "releases/\{\{rel_vsn\}\}/sys.config"}, diff --git a/share/server/60/rewrite_fun.js b/share/server/60/rewrite_fun.js index 1b27a9d14..436fd3da1 100644 --- a/share/server/60/rewrite_fun.js +++ b/share/server/60/rewrite_fun.js @@ -28,7 +28,7 @@ function rewriteFunInt(fun) { // If we have a function declaration without an Id, wrap it // in an ExpressionStatement and change it into - // a FuntionExpression + // a FunctionExpression if (decl.type == "FunctionDeclaration" && decl.id == null) { decl.type = "FunctionExpression"; ast.body[idx] = { @@ -41,7 +41,6 @@ function rewriteFunInt(fun) { return escodegen.generate(ast); } - function rewriteFun(funJSON) { const fun = JSON.parse(funJSON); return JSON.stringify(rewriteFunInt(fun)); @@ -53,4 +52,4 @@ function rewriteFuns(funsJSON) { return rewriteFunInt(fun); }); return JSON.stringify(results); -}
\ No newline at end of file +} diff --git a/share/server/60/rewrite_fun_ast_bypass.js b/share/server/60/rewrite_fun_ast_bypass.js new file mode 100644 index 000000000..d2c43caf1 --- /dev/null +++ b/share/server/60/rewrite_fun_ast_bypass.js @@ -0,0 +1,61 @@ +// 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. +// +// Based on the normalizeFunction which can be +// found here: +// +// https://github.com/dmunch/couch-chakra/blob/master/js/normalizeFunction.js + +function rewriteFunInt(fun) { + // Skip lengthy AST transforms if the function passed can be + // safely wrapped in parentheses. + if (fun.startsWith('function') && fun.endsWith('}')) { + return '(' + fun + ')' + } + + const ast = esprima.parse(fun); + let idx = ast.body.length - 1; + let decl = {}; + + // Search for the first FunctionDeclaration beginning from the end + do { + decl = ast.body[idx--]; + } while (idx >= 0 && decl.type !== "FunctionDeclaration"); + idx++; + + // If we have a function declaration without an Id, wrap it + // in an ExpressionStatement and change it into + // a FunctionExpression + if (decl.type == "FunctionDeclaration" && decl.id == null) { + decl.type = "FunctionExpression"; + ast.body[idx] = { + type: "ExpressionStatement", + expression: decl + }; + } + + // Generate source from the rewritten AST + return escodegen.generate(ast); +} + +function rewriteFun(funJSON) { + const fun = JSON.parse(funJSON); + return JSON.stringify(rewriteFunInt(fun)); +} + +function rewriteFuns(funsJSON) { + let funs = JSON.parse(funsJSON); + const results = Array.from(funs, (fun) => { + return rewriteFunInt(fun); + }); + return JSON.stringify(results); +} diff --git a/src/docs/src/config/query-servers.rst b/src/docs/src/config/query-servers.rst index 1d81665b8..3bd99c439 100644 --- a/src/docs/src/config/query-servers.rst +++ b/src/docs/src/config/query-servers.rst @@ -65,6 +65,19 @@ you can adjust the memory limitation (here, increasing to 512 MiB):: For more info about the available options, please consult ``couchjs -h``. +.. note:: + CouchDB versions 3.0.0 to 3.2.2 included a performance regression for + custom reduce functions. CouchDB 3.3.0 and later come with an experimental + fix to this issue that is included in a separate ``.js`` file. + + To enable the fix, you need to define a custom ``COUCHDB_QUERY_SERVER_JAVASCRIPT`` + environment variable as outlined above. The path to ``couchjs`` needs to + remain the same as you find it on your ``couchdb`` file, and the path to + ``main.js`` needs to be set to ``/path/to/couchdb/share/server/main-ast-bypass.js``. + + With a default installation on Linux systems, this is going to be + ``COUCHDB_QUERY_SERVER_JAVASCRIPT="/opt/couchdb/bin/couchjs /opt/couchdb/share/server/main-ast-bypass.js"`` + .. _Mozilla SpiderMonkey: https://spidermonkey.dev/ .. seealso:: diff --git a/support/build_js.escript b/support/build_js.escript index 5f1e92015..5ff45faed 100644 --- a/support/build_js.escript +++ b/support/build_js.escript @@ -68,11 +68,13 @@ main([]) -> _ -> [ "share/server/60/esprima.js", - "share/server/60/escodegen.js", - "share/server/60/rewrite_fun.js" + "share/server/60/escodegen.js" ] end, + RewriteFunFile = ["share/server/60/rewrite_fun.js"], + RewriteFunWithASTBypassFile = ["share/server/60/rewrite_fun_ast_bypass.js"], + Pre = "(function () {\n", Post = "})();\n", @@ -85,6 +87,13 @@ main([]) -> file:write_file(To, FinalBin) end, - ok = Concat(ExtraFiles ++ JsFiles, "share/server/main.js"), + case SMVsn of + "1.8.5" -> + ok = Concat(ExtraFiles ++ JsFiles, "share/server/main.js"), + ok = Concat(ExtraFiles ++ JsFiles, "share/server/main-ast-bypass.js"); + _ -> + ok = Concat(RewriteFunFile ++ ExtraFiles ++ JsFiles, "share/server/main.js"), + ok = Concat(RewriteFunWithASTBypassFile ++ ExtraFiles ++ JsFiles, "share/server/main-ast-bypass.js") + end, ok = Concat(ExtraFiles ++ CoffeeFiles, "share/server/main-coffee.js"), ok. |