我有一个带有~70个可执行文件测试的回购。在mocha或jest下运行时,它通常会在前几个承诺上出现错误,或者是因为超时,或者是因为stdout从未将其返回到父进程。
我对此问题的最小复制涉及100个线程,每个线程调用一个睡眠时间为10秒的命令行:
let child_process = require('child_process');
let AllTests = [];
/* start processes */
for (let i = 0; i < 100; ++i) {
AllTests.push({
i: i,
start: new Date(),
exec: new Promise((resolve, reject) => {
let program = child_process.spawn(
'node', ['-e', 'setTimeout(() => { process.exit(0); }, 9999)'])
// 'node', ['-e', 'for (let i = 0; i < 2**28; ++i) ;'])
program.on('exit', exitCode => { resolve({exitCode:exitCode}) })
program.on('error', err => { reject(err) })
})
})
}
/* test results */
describe('churn', () => {
AllTests.forEach(test => {
it('should execute test ' + test.i + '.',
done => {
test.exec.then(exec => {
test.end = new Date()
done()
})
})
})
})
在我的笔记本电脑上,我通常会得到:
93 passing (19s)
7 failing
1) churn
should execute test 0.:
Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/home/eric/errz/flood.js)
...
之后添加一些会计:
after(() => {
console.log()
AllTests.forEach(test => {
console.log(test.i, (test.end - test.start)/1000.0)
})
})
表明每个过程需要大约19秒。
鉴于这种情况发生在Mocha和Jest中,我猜这个问题与100个同步进程有关。建议?
答案 0 :(得分:0)
我几乎可以分别解决超时和stdio流问题。 当我将进程终止的退出处理程序推送到下一个事件周期时,流问题大多已清除:
program.on("exit", function(exitCode) {
setTimeout(
() => resolve({stdout:stdout, stderr:stderr, exitCode:exitCode}), 0
)
});
program.on("error", function(err) { reject(err); });
超时是因为我淹没了进程表。 我使用timeout-promise-queue而不是与内核的调度程序过于亲密,它使整个并发进程受到限制,并根据每个排队进程的开始时间提供超时。
使用timeout-promise-queue也清除了恶意流问题,这些问题仅在进程表变得过大时出现。 经过数千次测试后,我在退出处理程序上确定了一个25的进程队列和一个0长度的超时。
resulting diffs非常简单且不言自明,我不再需要在Travis tests上点击[LinkedInRestart作业]。