
CASE F1 :: 正常路径：获取 → 写入 → 续租 → 释放 → 下一持有者获取更大 token 并写入
  ok   holder-a 获取租约，token=1
  ok   holder-a 以 token=1 写入被接受
  ok   续租成功，token 不变
  ok   holder-a 主动释放
  ok   释放后 holder-b 立即获取，token=2
  ok   holder-b 以 token=2 写入被接受
  ok   报表内容来自 holder-b (expected=rows=121 by holder-b token=2, actual=rows=121 by holder-b token=2)
  ok   资源端 fence_floor=2 (expected=2, actual=2)

CASE F2 :: 边界：有效期内抢占、错 token 续租、过期后续租、相等/更小 token、事务内 now() 冻结
  ok   有效期内 holder-b 获取被拒绝
  ok   被拒绝的获取不推进 token (expected=1, actual=1)
  ok   持有者名正确但 token 错误：续租被拒绝
  ok   token 正确但持有者错误：续租被拒绝
  ok   过期后原持有者续租被拒绝，必须重新获取
  ok   原持有者重新获取，得到新 token=2
  ok   token=2 首次写入接受
  ok   相等 token=2 再次写入接受（同一持有期内多次写）
  ok   更小 token=1 写入被拒绝
  ok   被拒绝的写入未改动内容 (expected=v2 by holder-a token=2, actual=v2 by holder-a token=2)
  ok   holder-c 获取 ttl=300ms 的租约，token=3
  ok   同一事务内 now() 不随时间前进 (expected=2026-10-21 12:43:13.769062+08, actual=2026-10-21 12:43:13.769062+08)
  ok   clock_timestamp() 已越过 now()
  ok   自动提交连接已判定过期，但长事务内的获取仍按事务开始时间判为未过期而拒绝
  ok   自动提交模式下获取成功，token=4

CASE F3 :: 16 个线程（各自连接）同时获取同一租约：新建、过期后、有效期内三轮
  ok   第 1 轮（新建）获取成功的线程数 (expected=1, actual=1)
  ok   第 1 轮获胜者的 token (expected=1, actual=1)
  ok   第 2 轮（过期后）获取成功的线程数 (expected=1, actual=1)
  ok   第 2 轮获胜者的 token (expected=2, actual=2)
  ok   第 3 轮（有效期内）获取成功的线程数 (expected=0, actual=0)
  ok   三轮共 48 次尝试后 token=2 (expected=2, actual=2)

CASE F4 :: 持有者 A（独立 JVM）拿到 token 后被 SIGSTOP；租约过期后 B（独立 JVM）写入；A 被 SIGCONT 后写入；资源端模式=FENCED_SCOPE
  ok   A 获取租约 (expected=ACQUIRED token=1, actual=ACQUIRED token=1)
  ok   A 写前检查本地截止时间通过，随后停在写入之前 (expected=LOCAL_CHECK_OK, actual=LOCAL_CHECK_OK)
  ok   A 未暂停期间（等待 3000ms，TTL=1500ms）续租维持租约
  ok   SIGSTOP 后 /proc 中 A 的状态为 T（续租线程同样停止） (expected=T, actual=T)
  ok   租约按数据库 now() 判定过期
  ok   B 获取租约，token=2 (expected=ACQUIRED token=2, actual=ACQUIRED token=2)
  ok   B 本地检查通过 (expected=LOCAL_CHECK_OK, actual=LOCAL_CHECK_OK)
  ok   B 写入被接受 (expected=WRITE accepted=true token=2, actual=WRITE accepted=true token=2)
  ok   B 写后续租成功 (expected=RENEW_AFTER_WRITE ok=true, actual=RENEW_AFTER_WRITE ok=true)
  ok   B 释放租约 (expected=RELEASED true, actual=RELEASED true)
  ok   B 进程退出码 (expected=0, actual=0)
  ok   A 进程退出码 (expected=0, actual=0)
  note A 恢复后：WRITE accepted=false token=1；RENEW_AFTER_WRITE ok=false
  note 最终 summary = rows=201(B) by holder-b token=2；fence_floor=2
  ok   A 恢复后续租失败（token 已被取代） (expected=RENEW_AFTER_WRITE ok=false, actual=RENEW_AFTER_WRITE ok=false)
  ok   A 的旧 token 写入被资源端拒绝 (expected=WRITE accepted=false token=1, actual=WRITE accepted=false token=1)
  ok   报表保持 B 的结果 (expected=rows=201(B) by holder-b token=2, actual=rows=201(B) by holder-b token=2)
  ok   fence_floor 保持 2 (expected=2, actual=2)
  ok   write_attempt 记录一次 A 的被拒尝试 (expected=1, actual=1)

CASE F5 :: 负向对照：持有者 A（独立 JVM）拿到 token 后被 SIGSTOP；租约过期后 B（独立 JVM）写入；A 被 SIGCONT 后写入；资源端模式=UNFENCED
  ok   A 获取租约 (expected=ACQUIRED token=1, actual=ACQUIRED token=1)
  ok   A 写前检查本地截止时间通过，随后停在写入之前 (expected=LOCAL_CHECK_OK, actual=LOCAL_CHECK_OK)
  ok   A 未暂停期间（等待 3000ms，TTL=1500ms）续租维持租约
  ok   SIGSTOP 后 /proc 中 A 的状态为 T（续租线程同样停止） (expected=T, actual=T)
  ok   租约按数据库 now() 判定过期
  ok   B 获取租约，token=2 (expected=ACQUIRED token=2, actual=ACQUIRED token=2)
  ok   B 本地检查通过 (expected=LOCAL_CHECK_OK, actual=LOCAL_CHECK_OK)
  ok   B 写入被接受 (expected=WRITE accepted=true token=2, actual=WRITE accepted=true token=2)
  ok   B 写后续租成功 (expected=RENEW_AFTER_WRITE ok=true, actual=RENEW_AFTER_WRITE ok=true)
  ok   B 释放租约 (expected=RELEASED true, actual=RELEASED true)
  ok   B 进程退出码 (expected=0, actual=0)
  ok   A 进程退出码 (expected=0, actual=0)
  note A 恢复后：WRITE accepted=true token=1；RENEW_AFTER_WRITE ok=false
  note 最终 summary = rows=200(A) by holder-a token=1；fence_floor=0
  ok   A 恢复后续租失败（token 已被取代） (expected=RENEW_AFTER_WRITE ok=false, actual=RENEW_AFTER_WRITE ok=false)
  ok   negative control exposes: 资源端不检查 token 时，A 的旧结果覆盖了 B 的结果

CASE F6 :: 同一交错下比较：检查与写入分离（负向对照） vs 条件 UPDATE 持行锁
  ok   A 已读到 fence_floor 并通过检查
  note check-then-write：B accepted=true，A accepted=true，最终 old(A) by holder-a token=1，fence_floor=1
  ok   negative control exposes: A 的检查结果在写入时已过时：旧 token 覆盖新结果，fence_floor 从 2 回退到 1
  ok   A 已推进 fence_floor 并停在写数据之前（事务未提交）
  ok   pg_stat_activity 中 B 的会话在行锁上等待 (expected=1, actual=1)
  ok   B 尚未返回
  ok   A 先提交（token=1 合法），B 随后对新版本行重新求值 1<=2，接受
  ok   最终内容来自更大 token (expected=new(B) by holder-b token=2, actual=new(B) by holder-b token=2)
  ok   fence_floor=2 (expected=2, actual=2)
  ok   B 已推进到 2 后，A 的 token=1 写入被拒绝

CASE F7 :: 栅栏粒度：B 只写了 summary，旧持有者 A 写尚未被 B 触及的 detail
  note FENCED_ROW：B summary accepted=true，A(token=1) detail accepted=true
  ok   negative control exposes: 行级比较只看被写的那一行；新行没有旧 token 可比，旧持有者写入成功
  note FENCED_SCOPE：B summary accepted=true，A(token=1) detail accepted=false
  ok   作用域级 fence_floor 拒绝旧 token 写入任何分片
  ok   detail 分片不存在 (expected=0, actual=0)

CASE F8 :: pg.sh restart-immediate 后：租约行、token 与 fence_floor 均由 WAL 恢复，token 不复用
  ok   重启前 holder-a 持有 token=2 (expected=2, actual=2)
  ok   重启前写入接受
  ok   restart-immediate 退出码（pg b3-26 restarted after immediate stop） (expected=0, actual=0)
  ok   租约行恢复：holder-a 仍持有 token=2（租约不随数据库连接断开而失效） (expected=holder-a/2, actual=holder-a/2)
  ok   fence_floor 恢复为 2 (expected=2, actual=2)
  ok   已提交写入仍在 (expected=before-crash by holder-a token=2, actual=before-crash by holder-a token=2)
  ok   holder-a 释放
  ok   重启后的下一次获取得到 token=3，不复用 (expected=3, actual=3)
  ok   holder-b 写入接受
  ok   holder-a 的 token=2 写入被拒绝

CASE F9 :: 外部发布替身不认识 token，只按业务键幂等：去重有效，但不能阻止旧持有者抢先
  ok   顺序 B→A：A 的重复发布被业务键去重
  ok   保留 B 的内容 (expected=B-final, actual=B-final)
  note 顺序 A→B：A accepted=true，B accepted=false，保留 A-stale
  ok   negative control exposes: 幂等只保证一次，旧持有者先到时保留的是旧内容
  ok   每个业务键恰好一条发布记录 (expected=2, actual=2)

SUMMARY passed=86 failed=0
