feat(worker): IO Worker 分道(WK-1)
io worker 进程内按任务声明的 lane 分文件 / 网络 / 维护三条道,各自并发(缺省 2/4/1)、 各自排队;维护道在文件道或网络道有排队/运行时让路(连续 10 分钟放行一个),调度器内联的 清理/回填/保留期裁剪也走维护道。并发在 设置 → 自动化 → 高级 热生效,env 覆盖;旧 WORKER_CONCURRENCY 映射到网络道。运行状态页按道显示运行中/并发/排队。归属表写进 docs/runbook/dev.md,ci 与注册表逐项核对。 Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
68f53390f6
commit
da33cad81d
38 files changed
+1240
-105
No files matched your search
@@ -8,11 +8,31 @@ import { SiteRateLimit } from "./sources.ts";
|
||||
|
||||
const Percent = z.number().min(1).max(100);
|
||||
|
||||
/** 并发槽上限(与 io worker 各道开的槽位数一致;改设置只在这个范围内热生效) */
|
||||
export const WORKER_LANE_MAX = 16;
|
||||
/**
|
||||
* io worker 三条道的并发(WK-1,docs/runbook/dev.md「IO Worker 分道」):改后约 5 秒内热生效,不用重启;
|
||||
* 设置了环境变量 WORKER_FILE_CONCURRENCY / WORKER_NETWORK_CONCURRENCY(旧 WORKER_CONCURRENCY)/
|
||||
* WORKER_MAINTENANCE_CONCURRENCY 时以环境变量为准。
|
||||
*/
|
||||
export const WorkerLaneSettings = z.object({
|
||||
/** 文件道:整理、硬链接/移动、ffprobe、校验 */
|
||||
file: z.number().int().min(1).max(WORKER_LANE_MAX),
|
||||
/** 网络道:站点、RSS、TMDB、下载器、搜索(单站限流另计) */
|
||||
network: z.number().int().min(1).max(WORKER_LANE_MAX),
|
||||
/** 维护道:清理、回填、对账;文件道 / 网络道有活时让路 */
|
||||
maintenance: z.number().int().min(1).max(WORKER_LANE_MAX),
|
||||
});
|
||||
export type WorkerLaneSettings = z.infer<typeof WorkerLaneSettings>;
|
||||
export const DEFAULT_WORKER_LANES: WorkerLaneSettings = { file: 2, network: 4, maintenance: 1 };
|
||||
|
||||
export const AutomationSettings = z.object({
|
||||
/** 自动化总开关:关闭后所有自动收录/下载/整理停止,只读查询与手动操作可用 */
|
||||
enabled: z.boolean(),
|
||||
/** 恢复后的只读对账模式(REQ §8.5):开启时禁止自动写入 */
|
||||
readOnlyReconcile: z.boolean(),
|
||||
/** io worker 分道并发;旧库缺省时补 DEFAULT_WORKER_LANES */
|
||||
workerLanes: WorkerLaneSettings.default(DEFAULT_WORKER_LANES),
|
||||
});
|
||||
export type AutomationSettings = z.infer<typeof AutomationSettings>;
|
||||
|
||||
@@ -564,7 +584,7 @@ export type SettingsKey = z.infer<typeof SettingsKey>;
|
||||
|
||||
/** 缺省设置(settings 表缺 key 或缺字段时使用) */
|
||||
export const DEFAULT_SETTINGS: GlobalSettings = {
|
||||
automation: { enabled: true, readOnlyReconcile: false },
|
||||
automation: { enabled: true, readOnlyReconcile: false, workerLanes: DEFAULT_WORKER_LANES },
|
||||
cache: {
|
||||
hardThresholdPercent: 90,
|
||||
resumePercent: 85,
|
||||
@@ -646,7 +666,10 @@ export const DEFAULT_SETTINGS: GlobalSettings = {
|
||||
|
||||
/** PATCH /settings:按分组部分更新;服务端与现值合并后用 GlobalSettings 整体再校验 */
|
||||
export const GlobalSettingsUpdate = z.object({
|
||||
automation: AutomationSettings.partial().optional(),
|
||||
automation: AutomationSettings.omit({ workerLanes: true })
|
||||
.partial()
|
||||
.extend({ workerLanes: WorkerLaneSettings.partial().optional() })
|
||||
.optional(),
|
||||
cache: CacheShape.partial().optional(),
|
||||
organize: OrganizeSettings.partial().optional(),
|
||||
retention: RetentionSettings.partial().optional(),
|
||||
@@ -709,7 +732,11 @@ export function mergeSettings(current: GlobalSettings, patch: GlobalSettingsUpda
|
||||
const { webSearch: wsPatch, ...agent } = patch.agent ?? {};
|
||||
const { apiKey: _wsApiKey, ...webSearch } = wsPatch ?? {};
|
||||
return {
|
||||
automation: { ...current.automation, ...patch.automation },
|
||||
automation: {
|
||||
...current.automation,
|
||||
...patch.automation,
|
||||
workerLanes: { ...DEFAULT_WORKER_LANES, ...current.automation.workerLanes, ...patch.automation?.workerLanes },
|
||||
},
|
||||
cache: { ...current.cache, ...patch.cache },
|
||||
organize: { ...current.organize, ...patch.organize },
|
||||
retention: { ...current.retention, ...patch.retention },
|
||||
@@ -825,6 +852,7 @@ export const SETTINGS_SECTIONS: readonly SettingsSectionDef[] = [
|
||||
fields: [
|
||||
"automation.enabled",
|
||||
"automation.readOnlyReconcile",
|
||||
"automation.workerLanes",
|
||||
"cache.hardThresholdPercent",
|
||||
"cache.resumePercent",
|
||||
"cache.checkIntervalSec",
|
||||
|
||||
Reference in new issue
Block a user