task-30: getTaskPollBackoffMs 递增序列
getTaskPollBackoffMs 签名改为 attempt 可选(省略视为 0,向后兼容), 序列 500×factor^attempt、封顶 maxMs 行为由任务 29 的 factor 配置驱动。 新增 8 个测试:attempt0/1/2 值、序列单调、封顶、省略参数默认、 负 attempt 当 0、自定义 factor。
This commit is contained in:
@@ -66,9 +66,9 @@ export function getTaskProgressCacheTtlMs() {
|
|||||||
return TASK_PROGRESS_VISIBLE_CACHE_MILLIS;
|
return TASK_PROGRESS_VISIBLE_CACHE_MILLIS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 第 attempt 次退避延迟:base × factor^attempt,封顶 max;attempt 非法时按首次(0)处理 */
|
/** 第 attempt 次退避延迟:base × factor^attempt,封顶 max;attempt 省略/非法时按首次(0)处理 */
|
||||||
export function getTaskPollBackoffMs(attempt: number) {
|
export function getTaskPollBackoffMs(attempt?: number) {
|
||||||
const safe = Number.isFinite(attempt) && attempt > 0 ? Math.floor(attempt) : 0;
|
const safe = Number.isFinite(attempt) && (attempt as number) > 0 ? Math.floor(attempt as number) : 0;
|
||||||
const factor = getTaskPollBackoffFactor();
|
const factor = getTaskPollBackoffFactor();
|
||||||
const scaled = config.backoffBaseMs * Math.pow(factor, safe);
|
const scaled = config.backoffBaseMs * Math.pow(factor, safe);
|
||||||
return scaled > config.backoffMaxMs ? config.backoffMaxMs : scaled;
|
return scaled > config.backoffMaxMs ? config.backoffMaxMs : scaled;
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
import { test } from 'node:test'
|
||||||
|
import assert from 'node:assert/strict'
|
||||||
|
import {
|
||||||
|
getTaskPollBackoffMs,
|
||||||
|
configureTaskPolling,
|
||||||
|
resetTaskPollingConfig,
|
||||||
|
} from '../src/shared/task-progress-config.ts'
|
||||||
|
|
||||||
|
test('test_backoff_attempt0_500', () => {
|
||||||
|
resetTaskPollingConfig()
|
||||||
|
assert.equal(getTaskPollBackoffMs(0), 500)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('test_backoff_attempt1_1000', () => {
|
||||||
|
resetTaskPollingConfig()
|
||||||
|
assert.equal(getTaskPollBackoffMs(1), 1000)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('test_backoff_attempt2_2000', () => {
|
||||||
|
resetTaskPollingConfig()
|
||||||
|
assert.equal(getTaskPollBackoffMs(2), 2000)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('test_backoff_sequence_monotonic', () => {
|
||||||
|
resetTaskPollingConfig()
|
||||||
|
const sequence = [0, 1, 2, 3, 4, 5].map((n) => getTaskPollBackoffMs(n))
|
||||||
|
assert.deepEqual(sequence, [500, 1000, 2000, 4000, 8000, 10000])
|
||||||
|
for (let i = 1; i < sequence.length; i++) {
|
||||||
|
assert.ok(sequence[i] >= sequence[i - 1], '序列单调不减')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test('test_backoff_capped_at_max', () => {
|
||||||
|
resetTaskPollingConfig()
|
||||||
|
assert.equal(getTaskPollBackoffMs(5), 10000)
|
||||||
|
assert.equal(getTaskPollBackoffMs(10), 10000)
|
||||||
|
assert.equal(getTaskPollBackoffMs(50), 10000)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('test_backoff_no_attempt_default', () => {
|
||||||
|
resetTaskPollingConfig()
|
||||||
|
const defaulted = getTaskPollBackoffMs(undefined as never)
|
||||||
|
assert.equal(defaulted, 500, '省略参数视为首次退避')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('test_backoff_negative_attempt_zero', () => {
|
||||||
|
resetTaskPollingConfig()
|
||||||
|
assert.equal(getTaskPollBackoffMs(-1), 500)
|
||||||
|
assert.equal(getTaskPollBackoffMs(-100), 500)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('test_backoff_custom_factor', () => {
|
||||||
|
resetTaskPollingConfig()
|
||||||
|
configureTaskPolling({ backoffFactor: 3 })
|
||||||
|
assert.equal(getTaskPollBackoffMs(0), 500)
|
||||||
|
assert.equal(getTaskPollBackoffMs(1), 1500)
|
||||||
|
assert.equal(getTaskPollBackoffMs(2), 4500)
|
||||||
|
assert.equal(getTaskPollBackoffMs(3), 10000, '超过 max 封顶')
|
||||||
|
resetTaskPollingConfig()
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user