task-9: 进度类请求默认 10s 超时(progressTimeoutMs 可配置覆盖,postTaskProgressBatch 接入)
This commit is contained in:
@@ -7,7 +7,7 @@
|
|||||||
type JavaApiResponse,
|
type JavaApiResponse,
|
||||||
unwrapJavaResponse,
|
unwrapJavaResponse,
|
||||||
} from "@/shared/api/http";
|
} from "@/shared/api/http";
|
||||||
import { getTaskProgressCacheTtlMs } from "@/shared/task-progress-config";
|
import { getTaskProgressCacheTtlMs, getTaskProgressTimeoutMs } from "@/shared/task-progress-config";
|
||||||
import { createTaskProgressRequestCache } from "@/shared/task-progress-request-cache";
|
import { createTaskProgressRequestCache } from "@/shared/task-progress-request-cache";
|
||||||
|
|
||||||
const JAVA_API_PREFIX = "/newApi/api";
|
const JAVA_API_PREFIX = "/newApi/api";
|
||||||
@@ -71,7 +71,9 @@ async function postTaskProgressBatch<T>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const requestPromise = unwrapJavaResponse(
|
const requestPromise = unwrapJavaResponse(
|
||||||
post<JavaApiResponse<T>, { taskIds: number[] }>(path, { taskIds: normalizedTaskIds }),
|
post<JavaApiResponse<T>, { taskIds: number[] }>(path, { taskIds: normalizedTaskIds }, {
|
||||||
|
timeout: getTaskProgressTimeoutMs(),
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
taskProgressResponseCache.set(cacheKey, data);
|
taskProgressResponseCache.set(cacheKey, data);
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ export const TASK_PROGRESS_VISIBLE_CACHE_MILLIS = 5000;
|
|||||||
export const TASK_PROGRESS_HIDDEN_CACHE_MILLIS = 30000;
|
export const TASK_PROGRESS_HIDDEN_CACHE_MILLIS = 30000;
|
||||||
export const TASK_POLL_BACKOFF_BASE_MS = 500;
|
export const TASK_POLL_BACKOFF_BASE_MS = 500;
|
||||||
export const TASK_POLL_BACKOFF_MAX_MS = 5000;
|
export const TASK_POLL_BACKOFF_MAX_MS = 5000;
|
||||||
|
export const TASK_PROGRESS_TIMEOUT_MS = 10000;
|
||||||
|
|
||||||
export interface TaskPollingConfig {
|
export interface TaskPollingConfig {
|
||||||
/** 退避基数(毫秒),必须为正数 */
|
/** 退避基数(毫秒),必须为正数 */
|
||||||
@@ -29,6 +30,8 @@ export interface TaskPollingConfig {
|
|||||||
foregroundRefreshEnabled?: boolean;
|
foregroundRefreshEnabled?: boolean;
|
||||||
/** 前台恢复延迟(毫秒),默认 0 */
|
/** 前台恢复延迟(毫秒),默认 0 */
|
||||||
foregroundRefreshDelayMs?: number;
|
foregroundRefreshDelayMs?: number;
|
||||||
|
/** 进度类请求超时(毫秒),默认 10s,必须为正数 */
|
||||||
|
progressTimeoutMs?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULTS: Required<TaskPollingConfig> = {
|
const DEFAULTS: Required<TaskPollingConfig> = {
|
||||||
@@ -36,6 +39,7 @@ const DEFAULTS: Required<TaskPollingConfig> = {
|
|||||||
backoffMaxMs: TASK_POLL_BACKOFF_MAX_MS,
|
backoffMaxMs: TASK_POLL_BACKOFF_MAX_MS,
|
||||||
foregroundRefreshEnabled: true,
|
foregroundRefreshEnabled: true,
|
||||||
foregroundRefreshDelayMs: 0,
|
foregroundRefreshDelayMs: 0,
|
||||||
|
progressTimeoutMs: TASK_PROGRESS_TIMEOUT_MS,
|
||||||
};
|
};
|
||||||
|
|
||||||
let config: Required<TaskPollingConfig> = { ...DEFAULTS };
|
let config: Required<TaskPollingConfig> = { ...DEFAULTS };
|
||||||
@@ -73,6 +77,10 @@ export function getTaskForegroundRefreshDelayMs() {
|
|||||||
return config.foregroundRefreshDelayMs;
|
return config.foregroundRefreshDelayMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getTaskProgressTimeoutMs() {
|
||||||
|
return config.progressTimeoutMs;
|
||||||
|
}
|
||||||
|
|
||||||
export function configureTaskPolling(partial: TaskPollingConfig) {
|
export function configureTaskPolling(partial: TaskPollingConfig) {
|
||||||
const next = { ...config, ...partial };
|
const next = { ...config, ...partial };
|
||||||
if (!(next.backoffBaseMs > 0)) {
|
if (!(next.backoffBaseMs > 0)) {
|
||||||
@@ -84,6 +92,9 @@ export function configureTaskPolling(partial: TaskPollingConfig) {
|
|||||||
if (!(next.foregroundRefreshDelayMs >= 0)) {
|
if (!(next.foregroundRefreshDelayMs >= 0)) {
|
||||||
throw new Error("恢复延迟不能为负数: " + next.foregroundRefreshDelayMs);
|
throw new Error("恢复延迟不能为负数: " + next.foregroundRefreshDelayMs);
|
||||||
}
|
}
|
||||||
|
if (!(next.progressTimeoutMs > 0)) {
|
||||||
|
throw new Error("progressTimeoutMs 必须为正数: " + next.progressTimeoutMs);
|
||||||
|
}
|
||||||
config = next;
|
config = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import { test } from 'node:test'
|
||||||
|
import assert from 'node:assert/strict'
|
||||||
|
import {
|
||||||
|
TASK_PROGRESS_TIMEOUT_MS,
|
||||||
|
getTaskProgressTimeoutMs,
|
||||||
|
configureTaskPolling,
|
||||||
|
resetTaskPollingConfig,
|
||||||
|
} from '../src/shared/task-progress-config.ts'
|
||||||
|
|
||||||
|
test('test_progress_timeout_default', () => {
|
||||||
|
resetTaskPollingConfig()
|
||||||
|
assert.equal(getTaskProgressTimeoutMs(), 10000, '进度请求默认 10s')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('test_progress_timeout_override', () => {
|
||||||
|
resetTaskPollingConfig()
|
||||||
|
configureTaskPolling({ progressTimeoutMs: 15000 })
|
||||||
|
assert.equal(getTaskProgressTimeoutMs(), 15000)
|
||||||
|
resetTaskPollingConfig()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('test_non_progress_timeout_30s', () => {
|
||||||
|
assert.equal(TASK_PROGRESS_TIMEOUT_MS, 10000, '进度超时常量应为 10s')
|
||||||
|
// 非进度请求默认超时由 http.ts 的 30000 保持,此处断言常量存在
|
||||||
|
assert.ok(TASK_PROGRESS_TIMEOUT_MS < 30000)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('test_timeout_config_name', () => {
|
||||||
|
resetTaskPollingConfig()
|
||||||
|
configureTaskPolling({ progressTimeoutMs: 8000 })
|
||||||
|
assert.equal(getTaskProgressTimeoutMs(), 8000, '配置键 progressTimeoutMs 生效')
|
||||||
|
resetTaskPollingConfig()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('test_progress_timeout_minimum', () => {
|
||||||
|
resetTaskPollingConfig()
|
||||||
|
assert.throws(
|
||||||
|
() => configureTaskPolling({ progressTimeoutMs: -1 }),
|
||||||
|
/超时.*正数|不能为负|progressTimeoutMs/,
|
||||||
|
'非法配置应抛错并保持原值',
|
||||||
|
)
|
||||||
|
assert.equal(getTaskProgressTimeoutMs(), 10000, '非法配置后保持默认值')
|
||||||
|
resetTaskPollingConfig()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('test_override_via_options', () => {
|
||||||
|
resetTaskPollingConfig()
|
||||||
|
configureTaskPolling({ progressTimeoutMs: 20000 })
|
||||||
|
assert.equal(getTaskProgressTimeoutMs(), 20000, '函数级配置覆盖生效')
|
||||||
|
resetTaskPollingConfig()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('test_timeout_zero', () => {
|
||||||
|
resetTaskPollingConfig()
|
||||||
|
assert.throws(() => configureTaskPolling({ progressTimeoutMs: 0 }), /正数|不能为负|progressTimeoutMs/)
|
||||||
|
assert.equal(getTaskProgressTimeoutMs(), 10000, '0 应被拒并保持默认')
|
||||||
|
resetTaskPollingConfig()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('test_default_config_freeze', () => {
|
||||||
|
resetTaskPollingConfig()
|
||||||
|
const snapshots = [getTaskProgressTimeoutMs(), getTaskProgressTimeoutMs()]
|
||||||
|
assert.deepEqual(snapshots, [10000, 10000], '默认值快照稳定')
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user