docs(tests): explain scheduler risk evidence and retry clocks
This commit is contained in:
@@ -12,6 +12,7 @@ const deferred = () => { let resolve, reject; const promise = new Promise((yes,
|
||||
// 推进有限轮微任务供调度泵运行;不推进墙上时间,也不替代 scheduler.settle 的收敛等待。
|
||||
const tick = async () => { for (let i = 0; i < 20; i++) await Promise.resolve(); };
|
||||
// 建立可替换端口的调度环境;mutate 使用账本副本发布状态,persist 仅记录快照,默认服务立即成功。
|
||||
// changes 中的 canvas 创建只是调度参与者标记;此夹具不构建/校验业务文档,create/delete 名称不代表实际节点增删。
|
||||
function setup(overrides = {}, initial) {
|
||||
let ledger = createLedger(initial), time = 1000, next = 0, timerId = 0;
|
||||
const timers = new Map(), calls = [], persisted = [], errors = [];
|
||||
@@ -89,7 +90,7 @@ test('storage failure never sends and keeps claims; explicit resume retries pers
|
||||
state.add('create'); state.scheduler.start(); await state.scheduler.settle();
|
||||
assert.equal(sent.length, 0); assert.equal(state.errors[0].message, 'quota'); assert.equal(state.scheduler.getClaims().size, 1);
|
||||
broken = false; await state.scheduler.resume(); await state.scheduler.settle();
|
||||
// Dispatch evidence means query first, even if the failed barrier preceded the network call.
|
||||
// 账本已有 dispatch-marked 风险证据,即使本测试知道网络尚未进入,恢复仍保守先查询,不能依赖进程外无法证明的观察。
|
||||
assert.equal(state.calls[0][0], 'query'); assert.equal(sent.length, 0); assert.equal(state.scheduler.getClaims().size, 0); state.scheduler.stop();
|
||||
});
|
||||
|
||||
@@ -234,6 +235,7 @@ test('batching retains independent lanes and does not cross an external dependen
|
||||
test('unknown batch preserves all IDs, claims, and exact bytes across query and retry; later edits stay mutable', async () => {
|
||||
const sent = [], state = setup({ compileBatch, submit: async request => { sent.push(request); return { outcome: 'unresolved' }; }, query: async () => ({ outcome: 'unresolved' }) });
|
||||
state.add('one'); state.add('two', ['canvas'], ['one']); state.scheduler.start(); await state.scheduler.settle();
|
||||
// 冻结后加入的 three 仍是独立可变操作,不能为了节省请求而并入原未知批次的 body。
|
||||
const original = sent[0]; state.add('three', ['canvas'], ['two']); await state.scheduler.settle();
|
||||
assert.equal(sent.length, 1); assert.equal(state.ledger().getSnapshot().operations.get('three').phase, 'pending');
|
||||
await state.advance(100); await state.advance(100);
|
||||
@@ -256,7 +258,6 @@ test('a fenced pump reports failed state publication without throwing or sending
|
||||
const state=setup({mutate(){throw Error('storage_leader_lost');}});state.scheduler.start();state.add('fenced');await tick();await state.scheduler.settle();assert.equal(state.calls.length,0);assert.equal(state.ledger().getSnapshot().requests.size,0);assert.equal(state.ledger().getSnapshot().operations.get('fenced').phase,'pending');assert.ok(state.errors.some(error=>error.message==='storage_leader_lost'));state.scheduler.stop();
|
||||
});
|
||||
|
||||
// Wall time can change independently of the elapsed time used by browser timers.
|
||||
// 分离墙上时间与定时器经过时间:可单独跳动 wall,或推进 elapsed 并返回旧回调来模拟迟到触发。
|
||||
function adjustableClock(initial=1000){
|
||||
let wall=initial,elapsed=0,next=0;const timers=new Map();
|
||||
@@ -273,6 +274,7 @@ test('wall-clock jumps recheck retry deadlines without releasing unknown claims
|
||||
});
|
||||
state.add('first');state.add('dependent',['canvas'],['first']);state.scheduler.start();await state.scheduler.settle();
|
||||
const frozen=state.ledger().getSnapshot().requests.get('request-1');assert.equal(frozen.retryAt,1100);
|
||||
// 定时器经过 100ms 不代表 wall 已到 retryAt;墙钟从 1000 回退到 800 后,还须重新等待 300。
|
||||
clock.setWall(800);clock.fireAfter(100);await state.scheduler.settle();
|
||||
assert.equal(calls.length,1);assert.equal(state.scheduler.getClaims().get('canvas'),frozen.id);
|
||||
assert.equal(clock.timers.size,1);assert.equal([...clock.timers.values()][0].delay,300);
|
||||
@@ -281,6 +283,7 @@ test('wall-clock jumps recheck retry deadlines without releasing unknown claims
|
||||
clock.setWall(5000);const staleCallbacks=clock.fireAfter(300);await tick();
|
||||
assert.equal(calls.at(-1)[0],'query');assert.equal(calls.at(-1)[1].body,frozen.body);assert.equal(calls.at(-1)[1].id,frozen.id);
|
||||
assert.equal(state.scheduler.getClaims().get('canvas'),frozen.id);assert.equal(state.ledger().getSnapshot().operations.get('dependent').phase,'pending');
|
||||
// 手工重复执行已出队回调,模拟取消无法阻止迟到触发;请求仍在查询中,不能并发再查一次。
|
||||
for(const callback of staleCallbacks)callback();await tick();assert.equal(calls.length,3);
|
||||
receipt.resolve({outcome:'applied'});await state.scheduler.settle();
|
||||
assert.deepEqual(calls.map(([action,request])=>[action,request.operationIds[0]]),[['submit','first'],['submit','independent'],['query','first'],['submit','dependent']]);
|
||||
|
||||
Reference in New Issue
Block a user