初始化提交:go比特版本精简
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const { runConcurrentItems } = require('../src/utils/concurrent_runner');
|
||||
|
||||
/**
|
||||
* 让并发任务主动让出事件循环,便于测试真实的滑动窗口并发度。
|
||||
*/
|
||||
function yieldEventLoop() {
|
||||
return new Promise(resolve => setImmediate(resolve));
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const accounts = Array.from({ length: 545 }, (_, index) => ({
|
||||
email: `user${index}@example.com`,
|
||||
}));
|
||||
const processed = new Set();
|
||||
const progress = [];
|
||||
let active = 0;
|
||||
let maxActive = 0;
|
||||
|
||||
const batch = await runConcurrentItems(accounts, async (account, index) => {
|
||||
processed.add(account.email);
|
||||
active += 1;
|
||||
maxActive = Math.max(maxActive, active);
|
||||
try {
|
||||
await yieldEventLoop();
|
||||
if (index === 109) throw new Error('DB write timeout: updateAccountStatusReason');
|
||||
return { email: account.email, ok: true };
|
||||
} finally {
|
||||
active -= 1;
|
||||
}
|
||||
}, {
|
||||
concurrency: 50,
|
||||
onError: (error, account) => ({
|
||||
email: account.email,
|
||||
ok: false,
|
||||
code: 'ACCOUNT_TASK_ERROR',
|
||||
msg: error.message,
|
||||
}),
|
||||
onProgress: ({ completed }) => {
|
||||
progress.push(completed);
|
||||
if (completed === 110) throw new Error('模拟前端进度通道短暂异常');
|
||||
},
|
||||
});
|
||||
|
||||
assert.strictEqual(processed.size, 545, '第 110 个账号失败后仍应调度完全部 545 个账号');
|
||||
assert.strictEqual(batch.completed, 545, '完成计数必须包含成功和失败账号');
|
||||
assert.strictEqual(batch.results.filter(Boolean).length, 545, '每个账号都必须留下明确结果');
|
||||
assert.strictEqual(batch.results[109].ok, false, '单账号写库超时应保留为该账号失败');
|
||||
assert.strictEqual(progress.at(-1), 545, '最终进度必须到达批次总数');
|
||||
assert.strictEqual(maxActive, 50, '业务并发仍应按用户设置跑满 50');
|
||||
|
||||
let paused = true;
|
||||
let pauseWaiters = [];
|
||||
const pauseStarted = [];
|
||||
const pausedBatchPromise = runConcurrentItems([1, 2, 3, 4], async item => {
|
||||
pauseStarted.push(item);
|
||||
await yieldEventLoop();
|
||||
return item;
|
||||
}, {
|
||||
concurrency: 2,
|
||||
waitUntilReady: async () => {
|
||||
while (paused) await new Promise(resolve => pauseWaiters.push(resolve));
|
||||
},
|
||||
});
|
||||
await new Promise(resolve => setTimeout(resolve, 20));
|
||||
assert.deepStrictEqual(pauseStarted, [], '暂停期间不得领取任何新项目');
|
||||
paused = false;
|
||||
const releasing = pauseWaiters;
|
||||
pauseWaiters = [];
|
||||
releasing.forEach(resolve => resolve());
|
||||
const resumedBatch = await pausedBatchPromise;
|
||||
assert.strictEqual(resumedBatch.completed, 4, '继续后必须处理完全部剩余项目');
|
||||
assert.deepStrictEqual(pauseStarted.sort(), [1, 2, 3, 4], '继续后不得遗漏项目');
|
||||
|
||||
console.log('concurrent_runner_resilience.test.js: PASS');
|
||||
})().catch(error => {
|
||||
console.error(error);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
Reference in New Issue
Block a user