Files
FT546-WCS/docs/runbooks/ft546-realtime-shadow-runbook.md

11 KiB
Raw Blame History

FT546 实时只读影子运行手册

1. 适用范围

本手册只用于运行独立的 ruoyi-wcs-shadow 进程,采集新调度算法的只读影子证据。

  • 现场 LEGACY WCS 始终是唯一生产调度者、PLC 写入者和 WMS 回调者。
  • 本阶段不切换 STANDARD,不允许 shadow 连接 PLC/WMS,也不允许写生产业务表。
  • 停止或移除 shadow 进程就是本阶段的回滚;不得停止或切换稳定 WCS。
  • 本地自动化测试不能替代 SQL Server、网络隔离或现场观察门禁。

2. 当前验证边界

门禁 当前状态 允许的结论
Java 单元、Spring 上下文、H2 E2E 可在本地执行 代码级只读链路和协议证据可验证
PowerShell 脚本生命周期 可用无网络 sleeper jar 本地执行 确认、哈希、PID 精确启停和离线导出逻辑可验证
SQL Server 集成 NOT_RUN 不得宣称数据库集成或现场就绪
PLC/WMS 网络 deny NOT_RUN 必须由目标 shadow 主机的新鲜审计报告证明
HIL 与现场影子窗口 NOT_RUN 不得部署或开始采集

application-shadow-local.yml 是外部配置模板,不是可直接运行的 H2 profile。H2 仍是 test-scope 依赖,且测试资源不会进入打包 jar;没有审批过的独立 SQL Server 环境时,不运行 jar 本地 smoke。

当前 shadow_hold_history 能跨重启恢复 wait edge 和连续 HOLD 计数,测试覆盖第三次相同 HOLD 后计数为 3。生产调度内核尚无消费该计数并触发 reroute 的策略,因此不得把“计数恢复”报告成“第三次 HOLD 已重规划”。该语义应在取得影子证据并明确路线/阻塞资源状态后单独设计。

3. 前置条件

开始任何现场操作前,逐项确认:

  1. 稳定 WCS 健康,实际制品、配置、PID、命令行和监听端口可读取。
  2. source 登录只能读取 shadow_ro.* 批准视图,无生产表 INSERT、UPDATE、DELETE、EXECUTE、ALTER、CONTROL 或所有权权限。
  3. evidence 数据库与 source 数据库物理分离,evidence 登录只在 evidence 数据库具备所需写权限。
  4. shadow 部署目录、日志目录、端口和 PID 文件均与稳定 WCS 分离。
  5. shadow 主机到 PLC 写端点和生产 WMS 回调端点已在网络层拒绝,并生成不超过 60 分钟的 JSON 审计报告。
  6. 已约定生产 DB 延迟、shadow CPU/内存、source lag 和 evidence 磁盘阈值。
  7. 已准备立即停止 shadow 的值守人员;不安排任何稳定 WCS 切换动作。

权限报告最小结构:

{
  "generatedAtUtc": "2026-08-14T10:00:00Z",
  "status": "PASS",
  "source": {
    "server": "source-host",
    "database": "FT546-wcs",
    "login": "ft546_shadow_reader",
    "approvedViewsReadable": true,
    "productionWritePermissionCount": 0
  },
  "evidence": {
    "server": "evidence-host",
    "database": "FT546-shadow",
    "login": "ft546_shadow_writer",
    "requiredWritesGranted": true
  }
}

网络 deny 报告最小结构:

{
  "generatedAtUtc": "2026-08-14T10:00:00Z",
  "status": "PASS",
  "plcEgressDenied": true,
  "wmsEgressDenied": true
}

报告必须来自已批准环境的实际检查。不得手工伪造 PASS。

4. 构建与本地验证

在隔离工作树执行:

mvn clean test
mvn -pl ruoyi-wcs-shadow -am -DskipTests package
powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass `
  -File .\tools\shadow\tests\Test-Ft546ShadowScripts.ps1

打包后确认 shadow jar 不含 H2 或测试 profile:

jar tf .\ruoyi-wcs-shadow\target\ruoyi-wcs-shadow-3.8.9.jar |
  Select-String 'h2|application-shadow-test|application-shadow-local'

预期没有匹配项。该检查证明打包边界,不证明现场可运行。

5. 捕获稳定 WCS 基线

使用现场实际路径和 PID,不用 Git 分支或远端引用代替已部署制品:

.\tools\shadow\Capture-StableWcsManifest.ps1 `
  -StableArtifact 'D:\wcs-stable\ruoyi-admin.jar' `
  -StablePid 1234 `
  -Configuration 'D:\wcs-stable\application.yml' `
  -OutputJson 'D:\shadow-evidence\stable-manifest.json'

归档输出 JSON。确认其中的 artifact/config SHA-256、PID、启动时间、命令行和监听端口与现场一致。

6. 准备 shadow 目录和配置

将打包的 shadow jar 和一份外部 site 配置放在独立部署目录。配置只允许以下网络相关内容:

  • loopback 状态端点;
  • source SQL Server 只读连接;
  • 独立 evidence SQL Server 写连接。

配置不得包含 PLC IP/port 或 WMS URL。密码使用受控环境变量或主机密钥设施,不写入 Git、预检 JSON、PID 文件或命令行。

Start-Ft546Shadow.ps1 会从成功预检向进程注入 artifact/config SHA-256、source server/database、loopback address/port 和磁盘阈值,供应用内 startup gate 复核。

7. 运行预检

.\tools\shadow\Test-Ft546ShadowPreflight.ps1 `
  -StableManifest 'D:\shadow-evidence\stable-manifest.json' `
  -ShadowArtifact 'D:\ft546-shadow\ruoyi-wcs-shadow.jar' `
  -Config 'D:\ft546-shadow\application-shadow-site.yml' `
  -DeploymentDirectory 'D:\ft546-shadow' `
  -PermissionReport 'D:\shadow-evidence\permission-report.json' `
  -NetworkDenyReport 'D:\shadow-evidence\network-deny-report.json' `
  -StatusUri 'http://127.0.0.1:18091/shadow/status' `
  -MinimumFreeBytes 10737418240 `
  -OutputJson 'D:\shadow-evidence\shadow-preflight.json'

预检会失败关闭并留下 JSON,检查内容包括:

  • 稳定 PID 健康且 artifact/config 哈希未变;
  • shadow jar/config 位于独立部署目录;
  • YAML 中 source/evidence server、database、username 与权限报告一致;
  • source/evidence 数据库不同;
  • 权限和网络 deny 报告为新鲜 PASS;
  • 状态端点为 loopback,端口不与稳定 WCS 冲突;
  • 配置无 PLC/WMS 端点;
  • evidence 磁盘空间达到阈值;
  • shadow 部署目录中不存在当前 shadow.pid.json。即使其中记录的进程已退出,陈旧 PID 文件也会使预检失败;必须先核验并按停机/归档流程处理,禁止覆盖后启动。

任何一项失败都不得启动。

7.1 处理陈旧 PID 记录

不得删除或覆盖 shadow.pid.json。先读取其中的 pid:如果该 PID 仍存在,只能执行第 10 节的 Stop-Ft546Shadow.ps1,由脚本校验启动时间、Java 可执行文件、shadow jar 哈希和 -jar 命令行;校验不一致时停止操作并升级处理。

只有在 Get-Process 连续两次(间隔至少 5 秒)均确认该 PID 不存在后,才可由两名复核人员将原记录改名为 shadow.stale.<UTC timestamp>.json。变更单必须保留原记录 SHA-256、两次查询输出、归档路径、操作者和复核人。归档后重新执行完整预检,旧的预检 JSON 不得复用。

8. 启动与初始观察

只有经过双人复核的成功预检才能启动:

$started = .\tools\shadow\Start-Ft546Shadow.ps1 `
  -ConfirmReadOnlyShadow `
  -PreflightJson 'D:\shadow-evidence\shadow-preflight.json'

$started
Invoke-RestMethod 'http://127.0.0.1:18091/shadow/status'

启动后立即重新核对:

  1. 稳定 PID、制品哈希、健康状态和 PLC writer 所有权均未变化。
  2. shadow PID 与稳定 PID 不同;PID 文件位于 shadow 部署目录。
  3. shadow 只监听预检中的 loopback 端口。
  4. source 审计只有批准视图的 SELECT;生产业务表写审计为零。
  5. PLC/WMS 网络监控没有 shadow 连接尝试。

先以低频轮询观察 15 分钟。只有稳定 WCS 指标不变、source 查询延迟在阈值内、source lag 正常、evidence 持续增长且磁盘安全时,才可延长观察窗口。

STARTING 表示尚无完整 cycle;READY 表示最近 cycle 完成;DEGRADED 必须按 reason code 调查。证据存储失败或碰撞会永久停止该进程继续读取,需停止并保留现场。

9. 立即停止条件

出现任一条件,立即停止 shadow,不等待当前观察窗口结束:

  • 稳定 WCS 健康、CPU、内存、线程、PID 或 PLC writer 所有权变化;
  • 生产 DB 延迟超过约定阈值,或出现锁等待/连接池压力;
  • source 权限、数据库身份或批准视图发生变化;
  • source lag 超出事实新鲜度,或连续 query timeout;
  • 任意 PLC/WMS 连接尝试或任意生产表写审计;
  • evidence 磁盘低于阈值、写失败、哈希碰撞或 evidence backlog 异常;
  • shadow CPU/内存超过约定上限;
  • 状态为 DEGRADED 且原因不能立即解释为短暂 source 故障。

10. 精确停止与回滚

.\tools\shadow\Stop-Ft546Shadow.ps1 `
  -PidFile 'D:\ft546-shadow\shadow.pid.json'

停止脚本只接受该 PID 文件记录的进程,并逐项验证 PID、启动时间、Java 可执行文件、shadow jar 哈希和 -jar 命令行。任何不一致都拒绝停止,不会按 java 进程名批量查杀。成功后 PID 记录改名归档为 shadow.stopped.<timestamp>.json。

回滚完成条件:shadow PID 不存在,稳定 PID/健康/PLC 所有权保持原值,网络与数据库审计无 shadow 副作用。稳定 WCS 从未停止或切换。

11. 导出证据

现场 SQL Server 模式使用集成认证,或把 SQL 密码短暂放入当前进程环境变量 FT546_SHADOW_EVIDENCE_PASSWORD:

.\tools\shadow\Export-Ft546ShadowEvidence.ps1 `
  -EvidenceServer 'evidence-host' `
  -EvidenceDatabase 'FT546-shadow' `
  -EvidenceUsername 'ft546_shadow_writer' `
  -RunId '00000000-0000-0000-0000-000000000000' `
  -OutputDirectory 'D:\shadow-evidence\export-20260814'

离线复核模式接受已审批工具生成的快照目录:

.\tools\shadow\Export-Ft546ShadowEvidence.ps1 `
  -SnapshotDirectory 'D:\shadow-evidence\approved-snapshot' `
  -RunId '00000000-0000-0000-0000-000000000000' `
  -OutputDirectory 'D:\shadow-evidence\export-20260814'

输出固定包含:

  • cycles.csv、cycles.json;
  • protocol-differences.csv;
  • algorithm-differences.csv;
  • insufficient-facts.csv;
  • startup-checks.json;
  • manifest.json、manifest.sha256。

校验:

$expected = (Get-Content 'D:\shadow-evidence\export-20260814\manifest.sha256').Split()[0]
$actual = (Get-FileHash 'D:\shadow-evidence\export-20260814\manifest.json' -Algorithm SHA256).Hash.ToLowerInvariant()
if ($expected -ne $actual) { throw 'EVIDENCE_MANIFEST_HASH_MISMATCH' }

将稳定 manifest、preflight JSON、权限报告、网络 deny 报告、启动/停止 PID 记录、状态响应、现场监控截图和导出目录作为同一批次归档。

12. 禁止事项

  • 不在生产数据库内创建 evidence schema 代替独立 evidence 数据库。
  • 不给 source 登录任何生产写权限。
  • 不把 ruoyi-admin 以 SHADOW 模式启动。
  • 不允许 shadow 配置 PLC/WMS endpoint。
  • 不用宽泛 Java 进程查杀命令代替 Stop-Ft546Shadow.ps1。
  • 不因为本地/H2 测试通过就跳过 SQL Server、网络或现场门禁。
  • 不把本阶段结果解释为 STANDARD 上线许可。