完善外部生成Worker动态扩缩容

新增外部生成controller进程角色与systemd服务

补齐队列统计procedure与spacetime-client绑定

更新生产部署脚本、健康巡检和server provision的worker/controller口径

新增容器worker smoke脚本并同步运维文档与团队记忆
This commit is contained in:
2026-06-12 15:21:35 +08:00
parent 69815d918a
commit 4a6c126366
30 changed files with 2030 additions and 28 deletions

View File

@@ -24,6 +24,7 @@
- `renew_external_generation_job_lease_and_return`worker 长任务执行期间按 `worker_id + lease_token` 续租,防止外部生成超过单次 lease 后被重复领取。
- `complete_external_generation_job_and_return`worker 成功后按 `worker_id + lease_token` 写入 `result_payload_json`,任务进入 `completed`
- `fail_external_generation_job_and_return`worker 失败后按 `worker_id + lease_token` 回写错误,并按 `max_attempts` 决定回到 `pending` 重试或进入 `failed`
- `get_external_generation_queue_stats_and_return`controller 读取队列积压、运行中任务和过期 lease 数量,用于计算 worker 目标实例数;该 procedure 只读 `external_generation_job`,不直接操作 systemd。
这个 Module 的 **Seam** 在 SpacetimeDB procedure + `spacetime-client` facade`api-server` HTTP role 和 worker role 都只依赖这个 Interface。外部 provider、OSS、计费补偿、玩法草稿回写仍留在 `api-server` worker implementation 内,不进入 SpacetimeDB reducer。
@@ -82,6 +83,7 @@ pending/running -> cancelled (预留)
- `api`:只启动 HTTP server。
- `external-generation-worker`:只启动外部生成 worker不监听 HTTP。
- `external-generation-controller`:只启动 worker controller不监听 HTTP也不直接执行外部生成任务。
- `all`:本地开发可同时启动 HTTP 与 worker。
worker 配置:
@@ -91,7 +93,17 @@ worker 配置:
- `GENARRATIVE_EXTERNAL_GENERATION_WORKER_POLL_INTERVAL_MS`:空队列轮询间隔。
- `GENARRATIVE_EXTERNAL_GENERATION_WORKER_LEASE_SECONDS`:任务 lease 时长worker 会按约三分之一 lease、最长 30 秒的间隔续租。该值应覆盖一次心跳网络抖动窗口,不需要大于完整外部生成链路耗时。
动态缩扩容方式:生产通过 `deploy/systemd/genarrative-external-generation-worker@.service` 或进程管理器启动更多 `external-generation-worker` 实例;无需改变 HTTP 进程数。缩容或发布重启 worker 时,进程收到 SIGINT/SIGTERM 后会停止 claim 新任务并等待当前任务完成;若进程被硬杀、机器断电或超过 systemd `TimeoutStopSec`,未完成任务会在 lease 过期后被其它 worker 重新领取。容器链路已有独立 `external-generation-worker` compose service扩 worker 必须扩这个 worker service不能只扩 `api-server` HTTP service。
controller 配置:
- `GENARRATIVE_EXTERNAL_GENERATION_CONTROLLER_MIN_WORKERS`:保底 worker 实例数,生产默认 `1`controller 不会主动停止 `@1`
- `GENARRATIVE_EXTERNAL_GENERATION_CONTROLLER_MAX_WORKERS`:自动扩容上限,生产模板默认 `8`
- `GENARRATIVE_EXTERNAL_GENERATION_CONTROLLER_TARGET_JOBS_PER_WORKER`:每个 worker 实例承担的目标未完成任务数,默认 `2`;目标实例数按 `claimable_pending + running_active + expired_running` 计算后夹在 min/max 之间,避免把已包含过期 running 的 `claimable_count` 重复计入。
- `GENARRATIVE_EXTERNAL_GENERATION_CONTROLLER_POLL_INTERVAL_MS`controller 轮询队列统计的间隔,默认 `10000`
- `GENARRATIVE_EXTERNAL_GENERATION_CONTROLLER_SCALE_DOWN_IDLE_ROUNDS`:连续多少轮无可领取、无运行中、无过期 running 后才允许缩容,默认 `6`;缩容每轮只停止最高编号的一个实例。
- `GENARRATIVE_EXTERNAL_GENERATION_CONTROLLER_SERVICE_TEMPLATE`systemd worker 模板,默认 `genarrative-external-generation-worker@{}.service`
- `GENARRATIVE_EXTERNAL_GENERATION_CONTROLLER_DRY_RUN`:只记录决策不执行 systemctl默认 `false`
动态缩扩容方式:生产默认由 `deploy/systemd/genarrative-external-generation-controller.service` 启动 `GENARRATIVE_PROCESS_ROLE=external-generation-controller`controller 读取 `get_external_generation_queue_stats_and_return` 后对 `genarrative-external-generation-worker@N.service` 执行精确 `systemctl start/stop`;无需改变 HTTP 进程数。controller 只操作 `@1..@MAX` 中的缺口或最高编号多余实例,保留 `@1` 作为保底 worker。缩容或发布重启 worker 时,进程收到 SIGINT/SIGTERM 后会停止 claim 新任务并等待当前任务完成;若进程被硬杀、机器断电或超过 systemd `TimeoutStopSec`,未完成任务会在 lease 过期后被其它 worker 重新领取。容器链路已有独立 `external-generation-worker` compose service扩 worker 必须扩这个 worker service不能只扩 `api-server` HTTP service。
## 已接入的拼图纵切
@@ -147,13 +159,14 @@ GENARRATIVE_PROCESS_ROLE=all npm run dev
curl -f http://127.0.0.1:<api-port>/healthz
```
本地同步排查可显式使用 `GENARRATIVE_EXTERNAL_GENERATION_MODE=inline npm run dev:api-server`,用于确认 provider、OSS 和 SpacetimeDB 写回链路本身是否可行;该模式不覆盖 worker 队列 smoke。生产 smoke 需要保持 `GENARRATIVE_EXTERNAL_GENERATION_MODE=queue`,并至少启动一个 `api` 角色一个 `external-generation-worker` 角色;发布脚本会在默认 worker pattern 下自动启用并启动 `genarrative-external-generation-worker@1.service`并等待 worker active。若 worker 数量归零,生成任务会保持 `queued/running`,不会由 HTTP 进程偷偷执行。
本地同步排查可显式使用 `GENARRATIVE_EXTERNAL_GENERATION_MODE=inline npm run dev:api-server`,用于确认 provider、OSS 和 SpacetimeDB 写回链路本身是否可行;该模式不覆盖 worker 队列 smoke。生产 smoke 需要保持 `GENARRATIVE_EXTERNAL_GENERATION_MODE=queue`,并至少启动一个 `api` 角色一个 `external-generation-worker` 角色和一个 `external-generation-controller` 角色;发布脚本会在默认 worker pattern 下自动启用并启动 `genarrative-external-generation-worker@1.service`重启并验活 `genarrative-external-generation-controller.service`。若 worker 数量归零,生成任务会保持 `queued/running`,不会由 HTTP 进程偷偷执行。
systemd 生产扩缩容示例:
systemd 生产 controller 与手动兜底示例:
```bash
systemctl enable --now genarrative-external-generation-worker@1.service
systemctl enable --now genarrative-external-generation-controller.service
systemctl start genarrative-external-generation-worker@2.service
systemctl stop genarrative-external-generation-worker@2.service
systemctl status 'genarrative-external-generation-worker@*.service'
systemctl status genarrative-external-generation-controller.service 'genarrative-external-generation-worker@*.service'
```