Skip to content

Commit

Permalink
fix(cloud-function): close microtaskMode option which cause fatal e…
Browse files Browse the repository at this point in the history
…rror
  • Loading branch information
maslow committed Nov 4, 2021
1 parent b8442e0 commit 963da1f
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 29 deletions.
4 changes: 2 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 7 additions & 6 deletions packages/cloud-function/src/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

// 函数执行耗时
Expand Down
7 changes: 6 additions & 1 deletion packages/cloud-function/src/function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
55 changes: 38 additions & 17 deletions packages/cloud-function/tests/engine.test.js
Original file line number Diff line number Diff line change
@@ -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
}
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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 = `
Expand All @@ -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)

// })

})
6 changes: 3 additions & 3 deletions packages/system-client/src/views/application/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down

0 comments on commit 963da1f

Please sign in to comment.