fix(ci): 僵尸run复活防护——重负载job自检+dedupe竞态增强 [P1] #1562

Merged
xiaoxia merged 3 commits from ci/selfcheck-zombie-run-and-dedupe into develop 2026-08-31 11:58:00 +08:00
Owner

背景

2026-08-30 CI 巡检实证 Gitea 1.26.4 两个调度问题叠加造成 CI 高峰变慢:

  1. 僵尸 run 复活:被 concurrency cancel 的 run,其已派发 job 会在数小时后被调度器复活重跑(attempt=2)。实测 run 42597(develop push)18:46 被取消,21:46 复活,与正常流水线并行跑了 1.5 小时全套测试 + staging 构建(当晚 23:20 高峰期 4核CI机 CPU 超卖,单测 14 分→30 分)
  2. 合并竞态窗口双跑:现有 dedupe-check 只判断「同 head_sha 有无在跑的 push 流水线」,竞态窗口内两边都通过判断后,push 侧先跑完时 PR 侧仍在重复全量测试

改动

1. 新增 scripts/ci/ci_run_selfcheck.sh

  • job 启动第一步实时查询本 run 的 conclusion:若为 cancelled/skipped(即 Gitea 复活的僵尸 job),立即 exit 78 退出,不烧 runner
  • API 故障(unknown)时不阻断,照常执行,保证网络抖动不影响正常 CI
  • 配套 PR #1561 的脚本层墙钟自保,双保险

2. ci-pipeline.yml 12 个重量级 job 首步插入自检

validate×3、unit-tests、integration-tests、frontend-lint、frontend-unit-test、build-pr、build-staging、staging-e2e、staging-api-tests、build-production

3. dedupe-check 情形2增强

同一 head_sha 的 push 流水线:不只是「在跑/排队」时跳过 PR 侧测试,push 流水线已 success 时同样跳过(消除竞态窗口双跑)

影响面

  • 纯 CI 脚本/工作流;生产部署 job(deploy-production/canary)逻辑未动,仅 build-production 加了自检(被取消的生产构建及时止损)
  • shell 语法 + YAML 解析均通过
  • 42597 复活 run 已于 8-30 22:54 现场处置(容器 kill + DB 状态修正)

🤖 Generated by [构建服务器运维] agent

## 背景 2026-08-30 CI 巡检实证 Gitea 1.26.4 两个调度问题叠加造成 CI 高峰变慢: 1. **僵尸 run 复活**:被 concurrency cancel 的 run,其已派发 job 会在数小时后被调度器复活重跑(attempt=2)。实测 run 42597(develop push)18:46 被取消,**21:46 复活**,与正常流水线并行跑了 1.5 小时全套测试 + staging 构建(当晚 23:20 高峰期 4核CI机 CPU 超卖,单测 14 分→30 分) 2. **合并竞态窗口双跑**:现有 dedupe-check 只判断「同 head_sha 有无**在跑**的 push 流水线」,竞态窗口内两边都通过判断后,push 侧先跑完时 PR 侧仍在重复全量测试 ## 改动 ### 1. 新增 `scripts/ci/ci_run_selfcheck.sh` - job 启动第一步实时查询本 run 的 `conclusion`:若为 `cancelled/skipped`(即 Gitea 复活的僵尸 job),立即 `exit 78` 退出,不烧 runner - API 故障(unknown)时**不阻断**,照常执行,保证网络抖动不影响正常 CI - 配套 PR #1561 的脚本层墙钟自保,双保险 ### 2. `ci-pipeline.yml` 12 个重量级 job 首步插入自检 validate×3、unit-tests、integration-tests、frontend-lint、frontend-unit-test、build-pr、build-staging、staging-e2e、staging-api-tests、build-production ### 3. dedupe-check 情形2增强 同一 head_sha 的 push 流水线:不只是「在跑/排队」时跳过 PR 侧测试,push 流水线**已 success** 时同样跳过(消除竞态窗口双跑) ## 影响面 - 纯 CI 脚本/工作流;生产部署 job(deploy-production/canary)逻辑未动,仅 build-production 加了自检(被取消的生产构建及时止损) - shell 语法 + YAML 解析均通过 - 42597 复活 run 已于 8-30 22:54 现场处置(容器 kill + DB 状态修正) 🤖 Generated by [构建服务器运维] agent

🚀 预览环境已部署

项目 详情
PR号 #1562
预览链接 https://pr-1562.preview.xiaoxiajianji.com
API环境 staging

💡 预览环境使用 staging API 数据,请勿在预览环境中操作重要数据。

🔄 每次提交新代码后预览环境会自动更新。

🗑️ PR 关闭或合并后,预览环境会自动清理。

🚀 **预览环境已部署** | 项目 | 详情 | |------|------| | PR号 | #1562 | | 预览链接 | [https://pr-1562.preview.xiaoxiajianji.com](https://pr-1562.preview.xiaoxiajianji.com) | | API环境 | staging | > 💡 预览环境使用 staging API 数据,请勿在预览环境中操作重要数据。 > > 🔄 每次提交新代码后预览环境会自动更新。 > > 🗑️ PR 关闭或合并后,预览环境会自动清理。
auto-approve-bot approved these changes 2026-08-30 23:40:34 +08:00
auto-approve-bot left a comment
Collaborator

CI全绿,自动审批通过。

CI全绿,自动审批通过。
auto-approve-bot approved these changes 2026-08-30 23:40:34 +08:00
auto-approve-bot left a comment
Collaborator

CI全绿,自动审批通过。

CI全绿,自动审批通过。
xiaoxia force-pushed ci/selfcheck-zombie-run-and-dedupe from d459693e00 to 2f61d5e92d 2026-08-31 00:36:36 +08:00 Compare
xiaoxia force-pushed ci/selfcheck-zombie-run-and-dedupe from 2f61d5e92d to ad4c08de8a 2026-08-31 01:37:47 +08:00 Compare
xiaoxia force-pushed ci/selfcheck-zombie-run-and-dedupe from ad4c08de8a to cfb9b5aa9b 2026-08-31 02:54:16 +08:00 Compare
xiaoxia added 3 commits 2026-08-31 11:28:53 +08:00
P1修复(2026-08-30 CI巡检实证):
Gitea 1.26.4 调度器bug:被 concurrency cancel 的 run,其 job 会在数小时后
被复活重跑(attempt=2)。实测 42597 号 run 18:46 被取消,21:46 复活,
与正常流水线并行烧了1.5小时 runner(Validate+UnitTests+staging构建全套),
合并高峰双流水线CPU超卖导致单测 14分→30分。

改动:
1. 新增 scripts/ci/ci_run_selfcheck.sh:job 启动第一步实时查本 run 状态,
   若 conclusion 为 cancelled/skipped(被复活的僵尸job)立即退出(exit 78),
   API 故障时不阻断(unknown 照常执行)
2. ci-pipeline.yml 12 个重量级 job(validate×3、unit/integration/frontend×2、
   build-pr/build-staging/staging-e2e/api-tests/build-production)首步插入自检
3. dedupe-check 情形2增强:同一 head_sha 的 push 流水线不只是在跑时去重,
   已 success 完成时 PR 侧测试同样跳过(消除竞态窗口双跑)
fix(ci): 修正自检step插入位置(独立step而非Checkout内部)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 3s
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 1m52s
AI Code Review / AI Code Review (pull_request) Successful in 2m29s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 2m7s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m54s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
PR Automation / Auto Approve on CI Green (pull_request) Successful in 4m59s
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 2m39s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 3m47s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 5m45s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 5m48s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 9m7s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 4m16s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 18m14s
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Successful in 26s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 3m50s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 2m40s
a2c4314905
上一版 selfcheck 被插进 Checkout code step 内部导致 shell/env 键重复,
Gitea 拒绝解析整个 workflow(DetectWorkflows: ignore invalid workflow)。
本版将 selfcheck 作为独立 step 放在 Checkout 之后。
xiaoxia force-pushed ci/selfcheck-zombie-run-and-dedupe from cfb9b5aa9b to a2c4314905 2026-08-31 11:28:53 +08:00 Compare
Collaborator

【阻塞级判定】

  • 是否存在阻塞级问题:否
  • 阻塞级问题数量:0 个

📊 审查概览

  • 整体评价:通过
  • 建议级问题数量:2 个

🔴 阻塞级问题(必须修复)

💡 改进建议(不阻塞合并)

  1. [scripts/ci/ci_run_selfcheck.sh: 29] 建议扩大僵尸进程拦截范围

    • 具体内容:当前脚本仅拦截 cancelledskipped 状态的 Run。如果 Run 的 conclusionsuccess(表示整个流水线已成功),此时仍有 Job 被调度执行,这绝对属于僵尸进程,应当拦截以避免资源浪费。建议在 case 语句的 cancelled|skipped 分支中加入 success
    • 修改建议:将 case "${CONCLUSION}" in 下的第一行改为 cancelled|skipped|success)
  2. [scripts/ci/ci_run_selfcheck.sh: 20] 考虑 Python 依赖缺失的容错

    • 具体内容:脚本依赖 python3 进行 JSON 解析。虽然大多数 CI 环境都包含 Python,但为了极致的健壮性,如果 Runner 镜像意外精简了 Python,脚本会报错退出。考虑到这是防御性脚本,建议在 set -u 前检查 python3 是否存在,或者改用 Bash 原生字符串处理(较复杂)或 jq(如果环境保证存在)。当前方案在通用 CI 环境下可接受,作为建议提出。

良好实践

  1. 防御性编程:在 ci_run_selfcheck.sh 中使用了 set -u 并对关键环境变量进行了非空检查,防止了空指针异常导致的脚本意外中断。
  2. API 调用安全curl 使用了 -f (fail) 和 --max-time 10,有效防止了网络请求挂起或因 HTTP 错误导致的逻辑误判。
  3. 逻辑清晰:针对 Gitea 1.26 的调度 Bug 实施了精准的 Run 状态检测,注释清晰解释了业务背景和修复意图。
  4. 幂等性设计:去重逻辑中增加了对 conclusion=='success' 的判断,有效解决了 Rebase/Fast-forward 合并时的竞态条件导致的重复测试问题。

格式检查通过 | 逻辑审查通过 | 性能良好


🤖 由 AI 代码审查机器人自动生成 | 2026-08-31 03:37:17 | 模型:

### 【阻塞级判定】 - 是否存在阻塞级问题:否 - 阻塞级问题数量:0 个 ### 📊 审查概览 - 整体评价:通过 - 建议级问题数量:2 个 ### 🔴 阻塞级问题(必须修复) 无 ### 💡 改进建议(不阻塞合并) 1. **[scripts/ci/ci_run_selfcheck.sh: 29] 建议扩大僵尸进程拦截范围** - 具体内容:当前脚本仅拦截 `cancelled` 和 `skipped` 状态的 Run。如果 Run 的 `conclusion` 为 `success`(表示整个流水线已成功),此时仍有 Job 被调度执行,这绝对属于僵尸进程,应当拦截以避免资源浪费。建议在 case 语句的 `cancelled|skipped` 分支中加入 `success`。 - 修改建议:将 `case "${CONCLUSION}" in` 下的第一行改为 `cancelled|skipped|success)`。 2. **[scripts/ci/ci_run_selfcheck.sh: 20] 考虑 Python 依赖缺失的容错** - 具体内容:脚本依赖 `python3` 进行 JSON 解析。虽然大多数 CI 环境都包含 Python,但为了极致的健壮性,如果 Runner 镜像意外精简了 Python,脚本会报错退出。考虑到这是防御性脚本,建议在 `set -u` 前检查 `python3` 是否存在,或者改用 Bash 原生字符串处理(较复杂)或 `jq`(如果环境保证存在)。当前方案在通用 CI 环境下可接受,作为建议提出。 ### ✅ 良好实践 1. **防御性编程**:在 `ci_run_selfcheck.sh` 中使用了 `set -u` 并对关键环境变量进行了非空检查,防止了空指针异常导致的脚本意外中断。 2. **API 调用安全**:`curl` 使用了 `-f` (fail) 和 `--max-time 10`,有效防止了网络请求挂起或因 HTTP 错误导致的逻辑误判。 3. **逻辑清晰**:针对 Gitea 1.26 的调度 Bug 实施了精准的 Run 状态检测,注释清晰解释了业务背景和修复意图。 4. **幂等性设计**:去重逻辑中增加了对 `conclusion=='success'` 的判断,有效解决了 Rebase/Fast-forward 合并时的竞态条件导致的重复测试问题。 --- ✅ 格式检查通过 | ✅ 逻辑审查通过 | ✅ 性能良好 --- <sub>🤖 由 AI 代码审查机器人自动生成 | 2026-08-31 03:37:17 | 模型: </sub> <!-- AI_CODE_REVIEW_AUTO_COMMENT -->
xiaoxia merged commit 107a391c47 into develop 2026-08-31 11:58:00 +08:00

🗑️ 预览环境已清理

PR #1562 已关闭或合并,对应的预览环境已被清理。

如有需要,可以重新打开 PR 来重新生成预览环境。

🗑️ **预览环境已清理** PR #1562 已关闭或合并,对应的预览环境已被清理。 > 如有需要,可以重新打开 PR 来重新生成预览环境。
Sign in to join this conversation.