From 963da1f23120ea4985ba3a595b239e119a0aae72 Mon Sep 17 00:00:00 2001 From: maslow Date: Thu, 4 Nov 2021 23:21:24 +0800 Subject: [PATCH] fix(cloud-function): close `microtaskMode` option which cause fatal error --- docker-compose.yml | 4 +- packages/cloud-function/src/engine.ts | 13 +++-- packages/cloud-function/src/function.ts | 7 ++- packages/cloud-function/tests/engine.test.js | 55 +++++++++++++------ .../src/views/application/index.vue | 6 +- 5 files changed, 56 insertions(+), 29 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 810586750a..b594fadfd5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -54,11 +54,11 @@ services: SYS_SERVER_SECRET_SALT: Rewrite_Your_Own_Secret_Salt_abcdefg1234567 SHARED_NETWORK: laf_shared_network LOG_LEVEL: debug - APP_SERVICE_IMAGE: lafyun/app-service:latest + APP_SERVICE_IMAGE: lafyun/app-service:0.6.8 ACCOUNT_DEFAULT_APP_QUOTA: 5 APP_SERVICE_DEPLOY_HOST: local-dev.host:8080 # `*.local-dev.host` always resolved to 127.0.0.1, used to local development APP_SERVICE_DEPLOY_URL_SCHEMA: 'http' - # DEBUG_BIND_HOST_APP_PATH: '${PWD}/packages/app-service' + DEBUG_BIND_HOST_APP_PATH: '${PWD}/packages/app-service' command: npx nodemon volumes: - /var/run/docker.sock:/var/run/docker.sock:ro diff --git a/packages/cloud-function/src/engine.ts b/packages/cloud-function/src/engine.ts index 7d3f2c7dec..22703592c1 100644 --- a/packages/cloud-function/src/engine.ts +++ b/packages/cloud-function/src/engine.ts @@ -46,16 +46,17 @@ export class FunctionEngine { let data = result if (typeof result?.then === 'function') { /** + * @TIP 若打开 microtaskMode: 'afterEvaluate' 选项,则需要将以下代码解注,当前这种情况有严重 bug,暂不打开,也不删此注释 * 由于 vm 内部的 microTasks queue 为空时会直接释放执行环境,后续 await 则会导致工作线程陷入黑洞, * 故需先给 vm 返回的 promise 设置 then 回调,使 microTasks queue 不为空,以维护 vm 执行环境暂不被释放 */ - const promise = new Promise((resolve, reject) => { - result - .then(resolve) - .catch(reject) - }) + // const promise = new Promise((resolve, reject) => { + // result + // .then(resolve) + // .catch(reject) + // }) - data = await promise + data = await result } // 函数执行耗时 diff --git a/packages/cloud-function/src/function.ts b/packages/cloud-function/src/function.ts index 8ca1d81896..162c4fdf2e 100644 --- a/packages/cloud-function/src/function.ts +++ b/packages/cloud-function/src/function.ts @@ -99,7 +99,12 @@ export class CloudFunction { filename: `CloudFunction.${this.name}`, timeout: this.timeout, displayErrors: true, - microtaskMode: 'afterEvaluate', + /** + * 若关闭此项,则异步中的死循环无法被 timeout 捕捉,且会让工作线程陷入黑洞, + * 若打开此项,则异步 IO 会让工作线程陷入黑洞, + * 两者取其轻,还是选择关闭此项。 + */ + // microtaskMode: 'afterEvaluate', contextCodeGeneration: { strings: false } diff --git a/packages/cloud-function/tests/engine.test.js b/packages/cloud-function/tests/engine.test.js index 17fae2c78c..edac457a48 100644 --- a/packages/cloud-function/tests/engine.test.js +++ b/packages/cloud-function/tests/engine.test.js @@ -1,14 +1,15 @@ const assert = require('assert') const { FunctionEngine } = require('../dist/engine') -const vm = require('vm') +const http = require('http') +const fs = require('fs/promises') describe('FunctionEngine', () => { const options = { filename: 'CloudFunction.test_name', timeout: 1000, - microtaskMode: 'afterEvaluate', + // microtaskMode: 'afterEvaluate', contextCodeGeneration: { strings: false } @@ -62,7 +63,6 @@ describe('FunctionEngine', () => { } ` const res = await engine.run(code, {}, options) - console.log(res.error.message) assert.strictEqual(res.data, undefined) assert.ok(res.error instanceof Error) @@ -85,20 +85,22 @@ describe('FunctionEngine', () => { assert.ok(res.time_usage > 1000) }) - it('run() with async timeout & microtaskMode === "afterEvaluate" should be ok', async () => { - const engine = new FunctionEngine(require) + // it('run() with async timeout & microtaskMode === "afterEvaluate" should be ok', async () => { + // // 如果不设置 microtaskMode: 'afterEvaluate' 这种情况当前会让程序陷入黑洞,尚无办法处理 + // const engine = new FunctionEngine(require) - const code = ` - exports.main = async function(ctx) { - Promise.resolve().then(() => { while(true) { }}) - } - ` - const res = await engine.run(code, {}, options) - - assert.ok(res.error) - assert.strictEqual(res.error.code, 'ERR_SCRIPT_EXECUTION_TIMEOUT') - assert.ok(res.time_usage > 1000) - }) + // const code = ` + // exports.main = async function(ctx) { + // Promise.resolve().then(() => { while(true) { }}) + // return 123 + // } + // ` + // const res = await engine.run(code, {}, options) + + // assert.ok(res.error) + // assert.strictEqual(res.error.code, 'ERR_SCRIPT_EXECUTION_TIMEOUT') + // assert.ok(res.time_usage > 1000) + // }) it('run() with timeout & microtaskMode === "afterEvaluate" should be ok', async () => { const engine = new FunctionEngine(require) @@ -113,7 +115,7 @@ describe('FunctionEngine', () => { assert.strictEqual(res.data, 123) }) - it('run() with contextCodeGeneration.strings === false should be ok', async () => { + it('run() with eval() should be ok', async () => { const engine = new FunctionEngine(require) const code = ` @@ -128,4 +130,23 @@ describe('FunctionEngine', () => { assert.strictEqual(res.error.message, 'Code generation from strings disallowed for this context') }) + // it('run() debug', async () => { + // // 如果设置 microtaskMode: 'afterEvaluate', 异步 Io 会让程序陷入黑洞 + // const engine = new FunctionEngine(require) + + // const code = ` + // const http = require('http') + // const fs = require('fs/promises') + + // exports.main = async function(ctx) { + // await fs.readFile('test') + + // return 123 + // } + // ` + // const res = await engine.run(code, {}, options) + // console.log(res) + + // }) + }) \ No newline at end of file diff --git a/packages/system-client/src/views/application/index.vue b/packages/system-client/src/views/application/index.vue index 6a978d0554..e70eaaf72c 100644 --- a/packages/system-client/src/views/application/index.vue +++ b/packages/system-client/src/views/application/index.vue @@ -271,9 +271,9 @@ export default { this.applications.joined = joined }, toDetail(app) { - const route_url = this.$router.resolve({ - path: `/app/${app.appid}/dashboard/index` - }) + // const route_url = this.$router.resolve({ + // path: `/app/${app.appid}/dashboard/index` + // }) // window.open(route_url.href, '_blank') this.$router.push({ path: `/app/${app.appid}/dashboard/index`