docs: add shadow v3 field operations package
This commit is contained in:
@@ -1,166 +1,89 @@
|
||||
# FT546WMS 现场影子测试操作手册
|
||||
# FT546WMS 影子 v3 现场操作手册
|
||||
|
||||
## 1. 适用范围
|
||||
## 1. 这次部署做什么
|
||||
|
||||
本手册用于分支 `test/ft546wms-shadow-from-main-20260811` 的现场测试。该分支基于远端稳定版本:
|
||||
目标分支:`test/ft546wms-shadow-v3-20260903`。
|
||||
|
||||
```text
|
||||
origin/main = 916ac6bda32a5b102a3181cc8c5dbd5319c5656d
|
||||
影子 v3 只观察算法建议,不改变 Legacy 分配结果。入库、自动出库、手动出库仍由原存储过程执行一次,原返回码、返回消息、库位和异常保持不变。正常业务请求只做固定大小的内存登记,不等待影子线程,也不在调用线程执行影子数据库、文件或同步日志操作。
|
||||
|
||||
影子后台会读取业务表,并且只写 `alg.*` 对象。它可能增加数据库 CPU 和 I/O,因此仍需低峰部署、监控和随时回滚。
|
||||
|
||||
## 2. 固定安全边界
|
||||
|
||||
- 现场只允许一个 FT546WMS JVM,稳定版和影子版不能同时运行。
|
||||
- 仓库固定为 `01`;原始数据中的 `1` 与 `01` 等价,`W02` 只用于诊断,不能覆盖配置。
|
||||
- SQL Server 必须使用 Windows 集成认证;脚本不接收用户名和密码。
|
||||
- 当前现场已有 v2 包,安装必须使用 `upgrade`,不能使用 `clean`。
|
||||
- v2 到 v3 升级保留 `alg.decision_log`;发现未知 `alg` 对象、清单不完整或 manifest 不匹配时脚本会停止。
|
||||
- 影子失败、过期、并发、超时、配置错误或输入缺失均 fail-open:Legacy 继续,影子样本作废。
|
||||
- 不执行正式 ACTIVE 切换,不增加 WCS/PDA/理库/预留/回放功能。
|
||||
|
||||
## 3. 部署前准备
|
||||
|
||||
现场负责人先记录:
|
||||
|
||||
- SQL Server 名称和数据库名;
|
||||
- 当前 v2 测试开始时间和时区;
|
||||
- IDEA 当前 Run Configuration;
|
||||
- 当前稳定实例 PID 和端口;
|
||||
- 本次交付给出的精确 v3 提交号 `<V3_COMMIT>`;
|
||||
- 应用日志目录。
|
||||
|
||||
部署前先导出现有 v2 证据。不要卸载 v2:
|
||||
|
||||
```cmd
|
||||
cd /d C:\project\FT546WMS-SHANDOW
|
||||
call scripts\shadow-v3-export.cmd C:\project\FT546WMS-SHANDOW "<SQL_SERVER>" "<DATABASE>" "<V2_START_UTC>" "C:\project\FT546WMS-v2-before-v3" "<LOG_DIR>"
|
||||
```
|
||||
|
||||
影子算法只提出候选结果并记录比较数据。原有 Legacy 存储过程仍执行一次且仅执行一次,Legacy 的库位分配、返回码和响应内容始终是最终结果。
|
||||
如果当前代码还没有 v3 导出脚本,先用既有导出方式保存 `alg.decision_log`、诊断结果、日志、分支和提交号,再继续。
|
||||
|
||||
影子模式仍会读取 Legacy 表,并会在 `alg` schema 中刷新读模型和写入决策日志。因此“只观察”表示不改变 Legacy 业务数据和决策权,不表示完全没有数据库 CPU、I/O 或执行时间开销。
|
||||
## 4. 按顺序部署 v3
|
||||
|
||||
## 2. 安全边界
|
||||
### 第一步:停止 IDEA 中的 v2 稳定实例
|
||||
|
||||
- 默认配置 `FORTUCKY_SHADOW_ENABLED=false`,未显式开启时不执行影子规划。
|
||||
- 启用时只允许 `FORTUCKY_ALGORITHM_MODE=SHADOW`;误设 `ACTIVE` 或 `LEGACY` 会导致应用拒绝启动。
|
||||
- 影子事务使用 `REQUIRES_NEW`、`READ_UNCOMMITTED` 和 3 秒超时,并且只在名为 `ft546-shadow-observer` 的 daemon 工作线程执行。
|
||||
- 专用执行器只有一个线程、队列容量为 0,拒绝策略不会回落到 Legacy 请求线程;忙时其他观察立即跳过。
|
||||
- Legacy 线程仅提交任务和通知内存结果,不等待影子规划或比较,也不执行任何影子数据库操作。
|
||||
- 影子异常、超时、输入缺失和比较日志写入失败都会 fail-open,不跳过 Legacy 流程。
|
||||
- 安装包不在 `dbo.bas_cargo_space`、`dbo.bas_stock`、`dbo.mydo_job` 或其他 Legacy 表上创建触发器。
|
||||
- 安装包不对 Legacy 业务表执行 `INSERT`、`UPDATE`、`DELETE` 或 `MERGE`。
|
||||
- 影子模式不调用预留、深位锁、理库、回放、移库完成、WCS 完成或 PDA 回调写入过程。
|
||||
- `alg.reservation` 和 `alg.deep_workflow_lock` 是规划器读取兼容表;影子包不安装修改它们的过程,SHADOW 模式也禁止进入这些写入口。
|
||||
记录停止时间,在 IDEA 点 Stop。用下面命令确认最多只有一个相关 JVM,最好为 0:
|
||||
|
||||
## 3. 交付文件
|
||||
|
||||
```text
|
||||
fortucky-algorithm/src/main/resources/db/sqlserver/install-shadow.sql
|
||||
fortucky-algorithm/src/main/resources/db/sqlserver/diagnostics-shadow.sql
|
||||
fortucky-algorithm/src/main/resources/db/sqlserver/uninstall-shadow.sql
|
||||
```cmd
|
||||
jps -lv | findstr /I "RuoYiApplication FT546WMS"
|
||||
```
|
||||
|
||||
只使用名称带 `-shadow` 的脚本。该分支已移除原算法通用的 `install.sql`、`diagnostics.sql` 和 `uninstall.sql`,避免现场误装 ACTIVE 数据库对象。
|
||||
停止和重新启动之间会有一次短暂停机,这是计划内中断。不要同时启动两个实例来消除停机,否则可能重复执行定时任务、消费或外部回调。
|
||||
|
||||
## 4. 上现场前必须完成的演练
|
||||
### 第二步:同步精确分支和提交
|
||||
|
||||
1. 在现场数据库的最近备份副本上启动远端 `origin/main`,完成一次入库和一次出库基线测试。
|
||||
2. 在备份副本执行 `install-shadow.sql`,确认脚本全部成功。
|
||||
3. 用影子分支完成同样的入库和出库,确认业务结果仍来自 Legacy。
|
||||
4. 导出 `alg.decision_log`,然后停止影子程序并执行 `uninstall-shadow.sql`。
|
||||
5. 再次启动远端 `origin/main`,重复基线测试。
|
||||
|
||||
未完成数据库副本演练,不要直接在现场生产库安装。
|
||||
|
||||
## 5. 准备两个独立 IDEA 工作区
|
||||
|
||||
当前开发机的本地 `main` 已与 `origin/main` 分叉,不能作为稳定版回滚源。先执行 `git fetch origin`,确认 `origin/main` 的提交号,再从远端提交准备只读稳定工作区。
|
||||
|
||||
在仓库主目录执行。目标目录必须不存在;如果已存在,先人工确认它的来源,不要覆盖或删除:
|
||||
|
||||
```powershell
|
||||
```cmd
|
||||
cd /d C:\project\FT546WMS-SHANDOW
|
||||
git fetch origin
|
||||
$ExpectedStableCommit = "916ac6bda32a5b102a3181cc8c5dbd5319c5656d"
|
||||
$RemoteMainCommit = git rev-parse origin/main
|
||||
if ($RemoteMainCommit -ne $ExpectedStableCommit) { throw "origin/main 已变化,停止部署并重新合并验证" }
|
||||
|
||||
$StableWorktree = "D:\workSpace\PROJECT_SPACE\FT546\FT546MWS\worktrees\stable-origin-main-20260811"
|
||||
if (Test-Path -LiteralPath $StableWorktree) { throw "稳定版 worktree 目标目录已存在,请人工核对" }
|
||||
git worktree add --detach $StableWorktree origin/main
|
||||
|
||||
$CheckedOutCommit = git -C $StableWorktree rev-parse HEAD
|
||||
if ($CheckedOutCommit -ne $ExpectedStableCommit) { throw "稳定版 worktree 提交校验失败" }
|
||||
if (git -C $StableWorktree status --porcelain) { throw "稳定版 worktree 不是干净状态" }
|
||||
git switch test/ft546wms-shadow-v3-20260903
|
||||
git pull --ff-only
|
||||
git rev-parse HEAD
|
||||
git status --porcelain
|
||||
```
|
||||
|
||||
两个 IDEA 窗口分别打开:
|
||||
`git rev-parse HEAD` 必须等于交付的 `<V3_COMMIT>`,`git status --porcelain` 必须没有输出。若不一致,停止部署。
|
||||
|
||||
```text
|
||||
稳定版:D:\workSpace\PROJECT_SPACE\FT546\FT546MWS\worktrees\stable-origin-main-20260811
|
||||
影子版:D:\workSpace\PROJECT_SPACE\FT546\FT546MWS\worktrees\shadow-from-main-20260811
|
||||
### 第三步:执行只读预检
|
||||
|
||||
```cmd
|
||||
call scripts\shadow-v3-preflight.cmd C:\project\FT546WMS-SHANDOW <V3_COMMIT> "<SQL_SERVER>" "<DATABASE>"
|
||||
```
|
||||
|
||||
为两个窗口分别创建 Run Configuration:
|
||||
只有看到 `PREFLIGHT_OK` 才能继续。预检检查分支、精确提交、干净工作树、main 祖先、JVM 数量、SQL 连接、v2/v3 manifest 和当前快照;它不修改数据库。
|
||||
|
||||
- `FT546WMS-STABLE-origin-main`:完全沿用当前稳定现场参数,不增加任何 `FORTUCKY_*` 环境变量。
|
||||
- `FT546WMS-SHADOW`:复制稳定配置的 JDK、profile、端口、数据库和 Redis 参数,只增加下一节列出的环境变量。
|
||||
### 第四步:升级数据库包
|
||||
|
||||
禁止同时运行两个实例。即使使用不同 HTTP 端口,两个 WMS 实例也可能重复执行定时任务、消费任务或发送外部回调。切换时必须先停止当前实例,确认进程退出和端口释放,再启动另一个实例。
|
||||
当前现场是 v2,只执行:
|
||||
|
||||
## 6. 安装影子数据库包
|
||||
|
||||
### 6.1 安装前检查
|
||||
|
||||
由 DBA 对现场数据库做可恢复的完整备份,并记录备份文件、时间和恢复负责人。随后执行:
|
||||
|
||||
```sql
|
||||
SELECT SCHEMA_ID(N'alg') AS alg_schema_id;
|
||||
|
||||
SELECT trigger_object.name AS trigger_name,
|
||||
schema_name(trigger_object.schema_id) AS trigger_schema,
|
||||
schema_name(parent_object.schema_id) AS parent_schema,
|
||||
parent_object.name AS parent_table
|
||||
FROM sys.triggers trigger_object
|
||||
JOIN sys.objects parent_object ON parent_object.object_id = trigger_object.parent_id
|
||||
WHERE trigger_object.is_ms_shipped = 0
|
||||
ORDER BY parent_schema, parent_object.name, trigger_schema, trigger_object.name;
|
||||
|
||||
SELECT schema_name(object.schema_id) AS schema_name,
|
||||
object.name AS object_name,
|
||||
object.type_desc
|
||||
FROM sys.objects object
|
||||
WHERE object.is_ms_shipped = 0
|
||||
ORDER BY schema_name(object.schema_id), object.type_desc, object.name;
|
||||
```cmd
|
||||
call scripts\shadow-v3-install.cmd "<SQL_SERVER>" "<DATABASE>" upgrade
|
||||
```
|
||||
|
||||
预期 `alg_schema_id` 为 `NULL`。安装脚本发现任何既有 `alg` schema 会以错误 51000 拒绝执行。如果现场已经存在 `alg` schema 或同名对象,停止安装并先确认所有权;不要覆盖,也不要执行本分支卸载脚本。
|
||||
只有全新测试数据库、确认没有 `alg` schema 时才允许使用 `clean`。现场生产库不要使用 `clean`。
|
||||
|
||||
把两份查询结果连同执行时间保存为安装前基线。现场可能已有 Legacy 自身的触发器,安装后的全库 DML 触发器集合必须与安装前完全一致;用户对象清单只能新增本包定义的 `alg.*` 对象。
|
||||
### 第五步:设置 IDEA 环境变量
|
||||
|
||||
### 6.2 执行安装
|
||||
|
||||
在低业务量时段使用 SQL Server Management Studio 打开并执行:
|
||||
|
||||
```text
|
||||
fortucky-algorithm/src/main/resources/db/sqlserver/install-shadow.sql
|
||||
```
|
||||
|
||||
脚本使用 `GO` 分批执行并在当前会话设置 `READ_UNCOMMITTED`。首次安装会全量建立读模型,可能扫描较多现场数据;如果副本演练时间超出可接受窗口,现场不要继续。
|
||||
|
||||
也可以使用已配置 Windows 集成认证的 `sqlcmd`:
|
||||
|
||||
```powershell
|
||||
sqlcmd -S "<SQL Server>" -d "<数据库>" -E -b -r1 -i "<仓库>\fortucky-algorithm\src\main\resources\db\sqlserver\install-shadow.sql"
|
||||
```
|
||||
|
||||
不要把数据库密码写进脚本、IDEA Run Configuration 截图或命令历史。
|
||||
|
||||
### 6.3 安装后检查
|
||||
|
||||
重新执行安装前的两份元数据查询。全库 DML 触发器结果必须完全一致,用户对象清单只能新增预期的 `alg.*` 对象。保存安装后结果和执行时间,再执行:
|
||||
|
||||
```sql
|
||||
SELECT schema_name(object.schema_id) AS schema_name,
|
||||
object.name,
|
||||
object.type_desc
|
||||
FROM sys.objects object
|
||||
WHERE schema_name(object.schema_id) = N'alg'
|
||||
ORDER BY object.type_desc, object.name;
|
||||
|
||||
SELECT warehouse_id,
|
||||
COUNT(*) AS lane_count,
|
||||
MAX(rebuilt_at) AS latest_rebuild_at
|
||||
FROM alg.lane_state
|
||||
GROUP BY warehouse_id;
|
||||
```
|
||||
|
||||
本次新增对象只能位于 `alg` schema;`lane_state` 应存在目标仓库的数据。如果安装部分失败,不要启动影子程序,也不要执行本分支卸载脚本:拒绝式卸载会因对象缺失而报错 51014。保留完整输出和安装前后对象清单,由 DBA 决定从备份恢复,或依据清单人工清理本次未完成安装。不要补写 manifest、补建空对象或修改卸载脚本来绕过保护;清理完成且 `SCHEMA_ID(N'alg')` 再次为 `NULL` 后,才能重新完整安装。
|
||||
|
||||
确认包标记唯一且版本准确:
|
||||
|
||||
```sql
|
||||
SELECT package_id, package_version, installed_at
|
||||
FROM alg.shadow_package_manifest;
|
||||
```
|
||||
|
||||
必须只有一行:`FT546WMS_SHADOW_ONLY` / `2026-08-12-1`。不一致时不要启动影子程序,也不要手工修改标记。
|
||||
|
||||
## 7. IDEA 启用影子模式
|
||||
|
||||
在 `FT546WMS-SHADOW` 的 Environment variables 中设置:
|
||||
在影子 Run Configuration 中使用:
|
||||
|
||||
```text
|
||||
FORTUCKY_SHADOW_ENABLED=true
|
||||
@@ -168,137 +91,101 @@ FORTUCKY_ALGORITHM_MODE=SHADOW
|
||||
FORTUCKY_ALGORITHM_DATABASE_WRITES_ENABLED=true
|
||||
FORTUCKY_ALGORITHM_WAREHOUSE_ID=01
|
||||
FORTUCKY_SHADOW_TIMEOUT_SECONDS=3
|
||||
FORTUCKY_ALGORITHM_LEGACY_FALLBACK_ON_TECHNICAL_ERROR=false
|
||||
FORTUCKY_SHADOW_SNAPSHOT_MAX_AGE_SECONDS=60
|
||||
FORTUCKY_SHADOW_QUIET_INTERVAL_MILLIS=1000
|
||||
```
|
||||
|
||||
将 `01` 替换为现场实际仓库号。保持 `FORTUCKY_SHADOW_WAREHOUSE_ID` 未设置,使影子入口统一回退到 `FORTUCKY_ALGORITHM_WAREHOUSE_ID`。不要设置 replay、tidy、ACTIVE 或 WCS callback 相关变量。
|
||||
不要设置 `ACTIVE`,不要添加数据库密码,不要设置 replay、tidy、reservation 或 WCS callback 开关。
|
||||
|
||||
`FORTUCKY_ALGORITHM_DATABASE_WRITES_ENABLED=true` 是影子读模型与 `alg.decision_log` 所必需的应用开关;当前影子代码路径只执行已安装的 `alg.*` 写入,它不把 Legacy 决策权交给算法。该开关不是数据库权限隔离,安全性还依赖本分支的 SHADOW 启动保护、删减后的 SQL 包和现场变更审计。
|
||||
### 第六步:只启动一次影子实例
|
||||
|
||||
切换步骤:
|
||||
在 IDEA 启动 v3。不要重复点击 Run。启动时后台会认领唯一实例并建立第一代快照;认领或刷新失败只会关闭影子可用性,不应阻止 Legacy 启动。
|
||||
|
||||
1. 记录稳定实例停止时间,停止 `FT546WMS-STABLE-origin-main`。
|
||||
2. 确认 Java 进程退出、端口释放,且没有第二个 WMS 实例运行。
|
||||
3. 启动 `FT546WMS-SHADOW`。
|
||||
4. 如果出现 `Shadow observation requires ...`,不要修改保护条件,直接回滚到稳定实例。
|
||||
5. 先做只读登录和查询冒烟检查,再由现场人员执行一笔受控入库和一笔受控出库。
|
||||
6. 确认 Legacy 返回码、库位和现场设备动作与稳定流程一致。
|
||||
7. 确认 `alg.decision_log` 出现 `INBOUND`/`OUTBOUND` 记录,并至少有受控业务出现相应的 `*_COMPARE` 记录。
|
||||
启动后检查:
|
||||
|
||||
影子任务在 Legacy 前提交,但 Legacy 不等待后台线程。线程调度和数据库时序可能导致影子读取与 Legacy 写入重叠,甚至在 Legacy 写入后读取;影子结果不是严格的“Legacy 前快照”,分析时必须结合时间和业务结果解释。
|
||||
|
||||
单线程零队列执行器忙时,被跳过的请求不会调用算法,也没有对应的计划/比较决策日志;如果规划或 Legacy 超过观察期限,也可能只有计划日志而没有比较日志。Legacy 存储过程仍按原流程立即执行。这是保护业务延迟和数据库负载的预期行为,不要据此重放业务请求。
|
||||
|
||||
## 8. 观察与停止条件
|
||||
|
||||
应用日志中的以下文本表示后台影子已失败开放,Legacy 仍继续执行:
|
||||
|
||||
```text
|
||||
Shadow inbound observation failed open
|
||||
Shadow outbound observation failed open
|
||||
Shadow inbound observation interrupted
|
||||
Shadow outbound observation interrupted
|
||||
Inbound shadow submission failed open
|
||||
Project outbound shadow submission failed open
|
||||
Exact-stock shadow submission failed open
|
||||
```cmd
|
||||
jps -lv | findstr /I "RuoYiApplication FT546WMS"
|
||||
```
|
||||
|
||||
出现单条告警不等于 Legacy 失败,但应记录时间和业务单号。测试前由现场负责人和 DBA 根据稳定时段基线写下响应延迟、SQL CPU/I/O、锁等待、连接数和 Shadow 告警频率阈值;未约定阈值时,只允许少量受控业务,不进入连续观察。满足任一条件立即停止影子测试并执行第 10 节回滚:
|
||||
只能看到一个 FT546WMS JVM。
|
||||
|
||||
- Legacy 入库或出库结果、返回码、消息或设备动作与稳定版不同;
|
||||
- 正常接口达到预先约定的延迟阈值,或同类 3 秒超时告警重复出现;
|
||||
- SQL Server CPU、I/O、锁等待或连接数出现不可接受增长;
|
||||
- `alg` 之外出现任何由本次测试新增的表、过程、触发器或数据修改;
|
||||
- 同一 Legacy 存储过程疑似被重复调用;
|
||||
- WMS、WCS、PDA 或现场操作出现异常。
|
||||
### 第七步:做只读和受控业务检查
|
||||
|
||||
实际进入影子规划的请求每次都会按仓库全量刷新读模型。应用在单个 JVM 内将观察周期上限限制为 1、队列容量为 0,其他并发观察立即跳过;这能限制重叠刷新,但 `READ_UNCOMMITTED` 和单并发都无法消除全表扫描带来的 CPU 和 I/O,所以必须从少量受控业务开始,不能直接长时间全量开放。若部署多个影子 JVM,该上限分别生效,现场仍只允许启动一个 WMS 实例。
|
||||
先完成登录、页面查询等只读检查,再由现场人员执行一笔受控入库、一笔自动出库和一笔手动出库。确认:
|
||||
|
||||
## 9. 数据采集与交付
|
||||
- Legacy 返回码、消息、库位和设备动作与稳定流程一致;
|
||||
- 没有相同存储过程重复执行;
|
||||
- `alg.shadow_snapshot_state` 为仓库 `01` 的 `PUBLISHED`;
|
||||
- `alg.shadow_instance_heartbeat` 只有当前实例;
|
||||
- `alg.decision_log` 出现新的计划/比较记录;
|
||||
- 应用日志中的影子 warning 不包含业务载荷或异常消息。
|
||||
|
||||
测试开始和结束时各执行一次:
|
||||
## 5. 立即停止条件
|
||||
|
||||
```text
|
||||
fortucky-algorithm/src/main/resources/db/sqlserver/diagnostics-shadow.sql
|
||||
出现任一情况立即执行第 7 节回滚:
|
||||
|
||||
- Legacy 结果、响应码、消息、库位或设备动作改变;
|
||||
- 同一个 Legacy 存储过程疑似执行两次;
|
||||
- 出现第二个 FT546WMS JVM;
|
||||
- `alg` 之外出现本次部署造成的数据写入或新对象;
|
||||
- WMS、WCS、PDA 或现场作业异常;
|
||||
- 正常接口延迟或数据库 CPU、I/O、锁等待超过现场阈值;
|
||||
- 快照长时间不是 `PUBLISHED`,或 `ERROR_*` 指标持续增长。
|
||||
|
||||
单条 `shadow_failure` 表示该影子样本失败开放,不等于 Legacy 失败;但必须记录时间并纳入分析。
|
||||
|
||||
## 6. 连续观察和七天导出
|
||||
|
||||
至少连续观察 7 天,覆盖正常业务高峰、入库、自动出库和手动出库。期间不要重放业务来补影子样本。
|
||||
|
||||
观察结束后运行:
|
||||
|
||||
```cmd
|
||||
cd /d C:\project\FT546WMS-SHANDOW
|
||||
call scripts\shadow-v3-export.cmd C:\project\FT546WMS-SHANDOW "<SQL_SERVER>" "<DATABASE>" "<V3_START_UTC>" "C:\project\FT546WMS-shadow-v3-export" "<LOG_DIR>"
|
||||
```
|
||||
|
||||
在 SSMS 中选择“结果输出到文件”,保留完整结果。该脚本只读,包含对象清单、读模型统计、Legacy 表行数/校验和、当前锁和决策日志。Legacy 表校验和会扫描数据,只在低业务量时执行,不要高频轮询。
|
||||
成功时显示 `EXPORT_OK`。输出目录包含:
|
||||
|
||||
测试结束、卸载之前单独导出原始决策记录:
|
||||
- `deployment.txt`:分支、提交、工作树;
|
||||
- `runtime-processes.txt`:JVM 进程;
|
||||
- `environment-variable-names.txt`:仅环境变量名,不含值;
|
||||
- `sql-diagnostics.txt`:manifest、快照、心跳、输入覆盖、阻塞;
|
||||
- `comparison-detail.txt`:比较汇总和完整决策行;
|
||||
- `runtime-metrics.txt`:准入、跳过、错误和时延桶;
|
||||
- `inventory-parity.txt`:Legacy 与已发布快照库存差异计数;
|
||||
- `shadow-warnings.txt`:日志目录中的结构化影子 warning。
|
||||
|
||||
```sql
|
||||
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
|
||||
把整个输出目录压缩后交给分析,不要包含密码、令牌或与测试无关的个人信息。只有七天数据通过分析后,才讨论正式切换;本分支本身不执行 ACTIVE 切换。
|
||||
|
||||
SELECT decision_id, warehouse_id, decision_type, input_version,
|
||||
result_code, reason, created_at
|
||||
FROM alg.decision_log
|
||||
WHERE created_at >= '<测试开始时间>'
|
||||
AND created_at < '<测试结束时间>'
|
||||
ORDER BY created_at, decision_id;
|
||||
## 7. 回滚
|
||||
|
||||
首选回滚只关闭影子,不卸载数据库对象:
|
||||
|
||||
1. 在 IDEA 停止 v3,记录停止时间;
|
||||
2. 设置 `FORTUCKY_SHADOW_ENABLED=false`;
|
||||
3. 完整重启一次;
|
||||
4. 检查只有一个 JVM;
|
||||
5. 做登录、查询、受控入库和出库冒烟检查。
|
||||
|
||||
更彻底的应用回滚是停止 v3,切回现场原稳定源码/提交并用原 Run Configuration 启动。无论哪种方式,都先恢复业务服务,再处理数据库对象。
|
||||
|
||||
`alg.*` 可以保留供取证,稳定 Legacy 不依赖它。不要把卸载当作紧急回滚步骤。
|
||||
|
||||
## 8. 可选卸载
|
||||
|
||||
只有满足全部条件才卸载:影子实例已停止、稳定服务已恢复、七天证据已导出并备份、DBA 已确认维护窗口。
|
||||
|
||||
```cmd
|
||||
call scripts\shadow-v3-uninstall.cmd "<SQL_SERVER>" "<DATABASE>" CONFIRM_UNINSTALL
|
||||
```
|
||||
|
||||
出库对比记录的 `reason` 会带出 `detail`、`project`、`product`、`tray`、`stock`、`snapshotSync`、`legacyMatches` 和 `shadowMatches`。其中 `legacyMatches` 是按 Legacy 有效库存条件计算的实时数量,`shadowMatches` 是影子快照中按项目/产品条件计算的数量;两者不一致时先检查快照同步时间,再判断业务映射。
|
||||
卸载器只接受精确口令 `CONFIRM_UNINSTALL`,并由 SQL 包再次校验 v3 manifest 和完整对象白名单。发现未知/缺失对象时会在删除前停止。不要修改 manifest 或脚本绕过保护。
|
||||
|
||||
交付分析的数据包至少包括:
|
||||
## 9. 自动化验证与残余风险
|
||||
|
||||
- 决策日志导出文件,保持原始列名和时间字段;
|
||||
- `diagnostics-shadow.sql` 开始和结束两份结果;
|
||||
- 应用日志中测试时间段的 Shadow 告警及其前后异常栈;
|
||||
- 测试开始/结束时间、时区、仓库号、入库次数、自动出库次数、手动出库次数;
|
||||
- 现场人员确认的 Legacy 最终库位、返回码及异常说明;
|
||||
- 当前分支提交号和构建 JAR 的 SHA-256。
|
||||
|
||||
不要提供数据库密码、访问令牌或与分析无关的个人信息。
|
||||
|
||||
## 10. 快速回滚
|
||||
|
||||
### 10.1 首选:切回远端稳定源码
|
||||
|
||||
1. 停止 `FT546WMS-SHADOW`,记录停止时间。
|
||||
2. 确认进程退出和端口释放。
|
||||
3. 在独立稳定工作区启动 `FT546WMS-STABLE-origin-main`。
|
||||
4. 执行登录、查询、受控入库和受控出库冒烟检查。
|
||||
|
||||
稳定版不引用 `alg.*`,因此应用回滚时无需立即卸载数据库对象。先恢复生产服务,再在维护窗口导出数据并卸载。
|
||||
|
||||
### 10.2 备用:仅关闭影子开关
|
||||
|
||||
如果稳定工作区暂时无法启动,可在影子 Run Configuration 中设置:
|
||||
|
||||
```text
|
||||
FORTUCKY_SHADOW_ENABLED=false
|
||||
```
|
||||
|
||||
然后完整重启应用。该方式保留影子分支代码,但所有影子入口会直接返回空观察结果。它不替代稳定版回滚,只用于临时恢复。
|
||||
|
||||
## 11. 完整卸载
|
||||
|
||||
仅在影子应用已停止、`alg.decision_log` 已导出且稳定版已恢复后执行。
|
||||
|
||||
使用 SSMS 打开并执行:
|
||||
|
||||
```text
|
||||
fortucky-algorithm/src/main/resources/db/sqlserver/uninstall-shadow.sql
|
||||
```
|
||||
|
||||
卸载脚本先校验包标记必须精确等于 `FT546WMS_SHADOW_ONLY` / `2026-08-12-1`,再校验 `alg` 顶层对象的名称和类型必须与本包白名单完全一致。发现未知对象、缺失对象、类型不符或标记不符时会在第一个 `DROP` 前拒绝执行,不删除任何对象。校验通过后才在事务内删除本分支拥有的 `alg` 过程、视图、表和 schema;失败会回滚,不修改 Legacy 业务行。执行后验证:
|
||||
|
||||
```sql
|
||||
SELECT SCHEMA_ID(N'alg') AS alg_schema_id;
|
||||
```
|
||||
|
||||
预期结果为 `NULL`。再次查询全库 DML 触发器集合,结果应与安装前保存的基线一致。
|
||||
|
||||
不要在影子应用运行时卸载。如果脚本报错 51011-51014,保存错误和对象清单,停止手工删除,由 DBA 判断恢复或清理方案。不要修改 manifest 来绕过保护。卸载后如需再次测试,必须重新执行完整安装和检查流程。
|
||||
|
||||
## 12. 已知残余风险
|
||||
|
||||
- 当前自动化验证是 Java 单元测试和 SQL 静态审计,不能替代真实 SQL Server 2016 副本演练。
|
||||
- `READ_UNCOMMITTED` 可能读到业务事务的中间状态;该结果只用于影子比较,不参与 Legacy 决策。
|
||||
- 零等待异步提交不保证严格的 Legacy 前置快照;影子读取可能与 Legacy 写入重叠或发生在其后。
|
||||
- 3 秒事务超时会触发 fail-open,但 JDBC 取消和 SQL Server 回滚完成时间不保证严格等于 3 秒。
|
||||
- 每次影子规划前的全量刷新可能增加数据库 CPU 和 I/O;现场必须监控并设置短测试窗口。
|
||||
- JVM 内单线程零队列执行器只避免同一实例的重叠刷新,不限制其他进程或数据库会话;禁止同时运行稳定版和影子版,也禁止运行多个影子实例。
|
||||
- 应用级 fail-open 能保护 Legacy 控制流,但不能消除数据库、网络或主机资源争用。
|
||||
|
||||
这些残余风险是采用“零 Legacy 触发器、每次规划前全量刷新”的代价,也是现场必须先小流量验证、随时可切回稳定源码的原因。
|
||||
- 本地单元/静态测试不能替代 SQL Server 2016 备份副本演练。
|
||||
- `READ UNCOMMITTED` 只用于观察,可能看到业务事务中间状态。
|
||||
- 单线程容量一队列限制影子后台并发,但不能消除数据库资源竞争。
|
||||
- 3 秒 worker 等待超时不等于 JDBC/SQL Server 一定在 3 秒内完成取消或回滚。
|
||||
- 影子调用线程不等待后台,但主机和数据库资源竞争仍可能间接影响 Legacy,需要现场监控。
|
||||
|
||||
Reference in New Issue
Block a user