You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

10 KiB

R01-D4-2 claim token 最终独立复验报告(冻结前最后一次)

日期:2026-09-16 分支:refactor/r01-structure-performance HEAD:b10c4a732740e42353ca826f63141c3dd2a49915(复验全程未变) 复验对象:docs/r01-d4-2-claim-fence-remediation-20260916.md 的 claim token 整改 性质:仅复验 —— 未修改生产代码、未修改既有测试、未暂存、未提交、未推送


0. 最终结论

R01-D4-2独立代码复验和真实MySQL/Docker增量黑盒全部通过,GET评分快照写入迁移可以冻结并进入精确暂存检查点。

FAIL = 0,SKIP = 0。 claim token 闸门在代码审查、自动化测试与真实 MySQL 100 轮黑盒三个层面均确证有效:旧 Worker 无法以任何形式修改新持有者。


1. 基线

检查项 结果
分支 refactor/r01-structure-performance ✔
HEAD b10c4a732740e42353ca826f63141c3dd2a49915 ✔(= b10c4a7)
暂存区 空 ✔
git diff --check 无空白错误 ✔
V1–V44 未修改 ✔
新增迁移 V45 为唯一本轮新增(V44 为 D4-1 产物)✔
JAR mydreamjob-server-0.0.1-SNAPSHOT.jar,2026-09-16 17:26:51(最新 clean verify 产出)✔

2. 独立代码审查

CandidateScoringRefreshTaskExecutor.java(153 行,逐行核对):

核查项 结论 证据
token 在领取 SQL 中原子生成并写入 ✔ :53 UUID.randomUUID().toString();:56 SET status='PROCESSING', processing_started_at=?, claim_token=?, ... 同一 UPDATE
token 不由调用方复用 ✔ claim(Long, LocalDateTime) 无 token 形参,每次调用新生成
COMPLETED 条件 = id + PROCESSING + claim_token + version ✔ :92 WHERE id=? AND status='PROCESSING' AND claim_token=? AND version=?
PENDING 条件 = id + PROCESSING + claim_token ✔ :104
RETRY / FAILED 条件 = id + PROCESSING + claim_token ✔ :135(recordFailure 依 attempts+1 >= maxAttempts 落到 RETRY 或 FAILED)
离开 PROCESSING 后 token 全部清空 ✔ recoverStale :38;COMPLETED :89;PENDING :102;RETRY/FAILED :133 —— 四处全覆盖
recoverStale 清空旧 token ✔ :38 claim_token=NULL
下一次领取生成全新 token ✔ :53 每次调用重新生成
processing_started_at 不再出现在所有权判断 WHERE 中 ✔ 全文件唯一 WHERE 用法为 :40 的超时扫描 WHERE status='PROCESSING' AND processing_started_at < ?;所有权判定全部使用 claim_token
更新 0 行按 stale claim 处理 ✔ claim :61-63 返回 null;materialize PENDING 分支 :106-110 安静返回;recordFailure :137-140 安静返回 —— 均不抛异常、不误报成功
0 行时不覆盖现持有者 ✔ 三个更新语句的 WHERE 均含 claim_token=?
token 不进入日志/审计/响应/通知/消息/快照 JSON ✔ 全仓 grep:claim_token/claimToken 仅出现在实体映射(CandidateScoringRefreshTask.java:73-74)、Executor 的 SQL/注释/record、以及 CandidateScoringRefreshWorker.java:91 的传参;refresh 包内无任何含 token 的日志语句

3. 自动化复验

命令 实际 预期 结论
mvn -Dtest="*CandidateScoringRefresh*Test" test 49 / 49,F=0 E=0 S=0 ≥ 49 ✔
关联回归(9 组通配符) 571 / 571,F=0 E=0 S=0,BUILD SUCCESS ≥ 571 ✔
mvn clean verify 2176 / 2176,F=0 E=0 S=0,BUILD SUCCESS ≥ 2176 ✔
python docs\verify_openapi.py 176 paths / 214 operations,All checks passed 176/214 ✔

日志:fv-fence.log、fv-regression.log、fv-clean-verify.log


4. 真实 MySQL 最小黑盒

脚本 fv-fence.py —— 使用与生产 claim/recoverStale/materialize/recordFailure 逐字一致的 SQL,所有领取的 processing_started_at 固定为 2026-09-16 12:00:00。

结果:PASS

A. 100 轮竞争

每轮:A 领取(token_A)→ 回收(token 清空)→ B 在相同时间值下重新领取(token_B)→ A 以 token_A 执行四种迟到写 → B 以 token_B 完成。

断言 结果
A 的 COMPLETED 更新 0 行 ✔ 100 轮均为 0
A 的 PENDING 更新 0 行 ✔
A 的 RETRY 更新 0 行 ✔
A 的 FAILED 更新 0 行 ✔
B 的 token / status / version 未被 A 修改 ✔ 逐轮比对 status='PROCESSING'、claim_token=token_B、version=version_B 均不变
B 正常完成 ✔ 每轮 B 完成后 status='COMPLETED' 且 token 清空
终态无永久 PROCESSING ✔
无重复完成、无任务丢失 ✔ 终态任务数 = 1

B. token 生命周期

  • recoverStale 后 claim_token 为 NULL ✔
  • 再次领取后 token 非空且不同于此前所有 token ✔

C. 无效 token 不可更新

场景 行数
空 token(claim_token='') 0 ✔
伪造 token(随机新 UUID) 0 ✔
旧 token(A 的 token_A) 0 ✔
NULL token 0 ✔

且四种尝试后任务状态完全未变(前后 st() 全等)✔

D. version 推进 → PENDING → 下一轮新 token 完成

  • 同一 claim 期间新请求推进 version → COMPLETED 更新 0 行(version 不匹配)
  • 当前 Worker 走 PENDING 回退 → 1 行,status='PENDING' 且 token 清空 ✔
  • 下一轮领取使用新 token ✔ → 完成 1 行,status='COMPLETED' ✔

5. 最小业务回归

实例:最新 JAR;物化实例 PID 11884(SCORING_REFRESH_ENABLED=true)、只读实例 PID 6988(false),均已停止。

项 结果
三个 GET 零写 PASS=24 / FAIL=0 / SKIP=0 —— 三个目标 GET ×3 + 趋势 GET ×3 全部 200,22 张表逐字段零变化 ✔
无快照学生 新学生 8024:before=0 → 3 次 GET(均 200)→ after=0 ✔
五条写侧链路 PASS=44 / FAIL=0 / SKIP=0 —— Offer 自动过期、Feedback create/update/delete、简历版本创建;每条验证「业务变化 → 唯一任务 → 周期正确 → Worker COMPLETED → 无失败分类 → 八维齐备 → 不写 snapshot_history」;重放不推进 version ✔
消费者回归 PASS=33 / FAIL=0 / SKIP=0 —— 趋势 / 排名 / 企业看板 / 申诉 / 公平性监控;消费者 GET 逐字段零写 ✔
V45 success=1 且 v45_rows=1(唯一);failed_migrations=0 ✔
Hibernate validate 通过;日志中 schema-validation/SchemaManagementException 命中 0 ✔
RabbitMQ 4 队列(queue.sms.verification / queue.notification / queue.notification.dlq / queue.audit)ready=0、unacked=0 ✔
token 泄漏扫描 PASS —— 日志非框架 UUID 命中 0;HTTP 响应 token 形态命中 0;数据库其它载体命中 0(当前无活跃 token,终态/PENDING 已全部清空)✔
扫描器探针 临时文件注入 UUID → 正则命中;向 audit_logs 插入探针 → DB 命中 1 → 清理后 0 —— 探针 PASS,证明扫描器非空转;探针文件已删除 ✔

6. 收尾

项 结果
本轮 Java 实例 仅停止本轮记录的两个 PID(11884、6988);tasklist java.exe 计数 = 0 ✔
8080 / 8081 空闲 ✔
Docker 保持运行,三容器 healthy ✔
数据清理 users 残留 0(8020/8021/8022/8023/8024 及公司、岗位 51、投递 94/95、offer、简历版本全部按精确主键删除)✔
append-only 审计 audit_logs 中 actor 为本轮 5 个隔离用户的 15 + 2 = 17 行保留并登记(关联 ID:user_id ∈ {8020, 8021, 8022, 8023, 8024})✔
测试夹具(精确 INSERT,已登记并已清理) user_roles 为 8022/8023 各插入 1 行 COMPANY ✔
Git 分支未变、HEAD 未变、暂存区空、V1–V44 未改、V45 唯一新增、无删除文件、无空白错误 ✔
未暂存 / 未提交 / 未推送 是 ✔

本轮新增产物(docs/acceptance/r01-d4-2-20260916/,未进入暂存区):fv-fence.py + fv-fence-out.txt、fv-tokenscan.py、fv-fence.log、fv-regression.log、fv-clean-verify.log、v3-fv-out.txt、s1-fv-out.txt、s4-fv-out.txt。


7. PASS / FAIL / SKIP 清单

FAIL:无。

SKIP:无。

PASS:

  1. 基线(分支/HEAD/暂存区/V1–V44/V45 唯一/最新 JAR)
  2. 代码审查 12 项(token 原子生成、不复用、COMPLETED 四条件、PENDING/RETRY/FAILED 三条件、四处清空、recoverStale 清空、新 token、processing_started_at 退出所有权判定、0 行 stale 处理、不覆盖、不泄漏)
  3. fence 定向 49/49
  4. 关联回归 571/571
  5. 全量 clean verify 2176/2176(S=0)
  6. OpenAPI 176/214
  7. 真实 MySQL 100 轮竞争(四种迟到写全 0 行、B 未被修改、B 均完成、无永久 PROCESSING、无丢失)
  8. token 生命周期(回收后 NULL、重领后新值)
  9. 空/伪造/旧/NULL token 均无法更新
  10. version 推进 → PENDING → 新 token 完成
  11. 三个 GET 零写 24/24(22 表逐字段)
  12. 无快照学生 0 行
  13. 五条写侧 44/44
  14. 消费者 33/33
  15. V45 成功且唯一
  16. Hibernate validate
  17. RabbitMQ 四队列为 0
  18. token 泄漏扫描 0 + 探针有效

8. 复验过程中的自暴露问题(非产品缺陷)

# 问题 处置
1 fv-fence.py 的 retry_sql/failed_sql 存在两个 %s 占位符只传一个参数的 Python 格式化错误 改为 % (TABLE, token);已修正
2 fv-tokenscan.py 的 DB 探针使用 messages 表触发外键失败 改用无外键依赖的 audit_logs,并增加「插入前=0 / 插入后=1 / 清理后=0」三段验证;已修正
3 探针插入的 audit_logs 行 探针执行后立即删除,已确认清理后计数为 0

以上均为验收脚本自身问题,与产品代码无关;修正后重跑全部通过。


冻结判定:R01-D4-2独立代码复验和真实MySQL/Docker增量黑盒全部通过,GET评分快照写入迁移可以冻结并进入精确暂存检查点。