13 KiB
schema, ticket, title, consumer, author, change_type, backend_status, gateway_status, frontend_status, frontend_owner, frontend_ref, target_release, verified_at, status_note, updated_at, base
| schema | ticket | title | consumer | author | change_type | backend_status | gateway_status | frontend_status | frontend_owner | frontend_ref | target_release | verified_at | status_note | updated_at | base |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| hl-changelog/v2 | 8002 | fleet: 保险生命周期锁自锁问题修复——拆出新错误码 605075(资源锁已失效) | admin | wx(GIT) | 修复 | deployed | not_required | not_required | 本条是错误码变更通知,无新增/修改/删除接口、无出入参变化。gateway_status=not_required 的确切含义与依据:本条不引入任何新路由,受影响端点的网关可达性已于 2026-09-20 在工单 #8002 AC-2 经网关实测(HTTP 200);而 605075 这条分支需要「持锁期间租约丢失」这一在 Redis 侧发生、无法按需构造的条件,因此**不存在该分支的网关读数**,代码侧证据是 4 个调用点与单测。⚠️ 这不等于「已验证 605075 会原样到达前端」——它只是说明网关验证在本条上不是一道可执行的闸门。frontend_owner 留空:认领位由前端侧回写,后端不代填。⚠️ 本文件的行号与占位符示例已于 2026-09-21 对 origin/dev-v3=c1a6e96c8 逐条复核订正过一轮,详见「八、相关历史 PR」下方的订正记录。前端实证 not_required(mmg 2026-09-21):605008/605075/系统繁忙 前端 src 全仓零命中;request.js 重试层只认网络错误/超时/408/429/5xx,业务码 HTTP 200 且非幂等方法默认不重试,不存在「把 605075 当系统繁忙自动重试」的路径;605075 经响应拦截器透后端 message(资源锁已失效(锁标签),本次操作未提交)与 605008 文案天然分叉,无需建码字典。 | 2026-09-21 | dev-v3 |
fleet: 保险生命周期锁自锁问题修复——拆出新错误码 605075(资源锁已失效)
服务: hl-fleet-service PR: #8015 + #8042(已合入 dev-v3,merge
44a40de90+8629bf5dd) Issue: #8002 日期: 2026-09-20、2026-09-21 影响范围: 管理后台派单、保险自动投退保相关接口新增错误码返回
⚠️ 关键变化
前端不能再把"资源锁已失效"当"系统繁忙"处理。
原先所有锁相关失败都返 605008("抢锁超时,系统繁忙,请稍后重试"),本次拆出 605075("资源锁已失效({0}),本次操作未提交")区分两种完全不同的故障模式:
| 错误码 | 原因 | 前端处置 |
|---|---|---|
| 605008 | 等待窗口内没抢到锁(别人正持有) | ✅ 重试有意义;提示"系统繁忙,请稍后重试" |
| 605075 | 持锁期间丢了(Redis 侧事件:主从切换、键被清、租期到期) | ❌ 重试通常无效;提示"数据一致性风险,请刷新确认或联系管理员" |
605008 语义已收窄:从"抢锁失败"统一码改为"等待超时"专用码,消除歧义。错误信息本身不变("抢锁超时,系统繁忙,请稍后重试"),但现在必定表示"重试可能成功"。
一、背景
派单与司机保险档案共用一把司机锁(司机保险生命周期锁)。锁是非重入的——同一事务里对同一司机调用两次会等待,最后必然超时(#8002 AC-1)。
#8002 通过事务内重入机制解决自锁问题,同时暴露了一个潜在风险:锁有有限租期(看门狗续期),如果续租在执行期间失败(Redis 主从切换、连接抖动、租期到期等),调用方无法察觉,仍会继续写入,可能产生与并发更新混合的脏数据。
本次新增 605075 把这类"租约丢失"的故障单独标记,让前端与运维据此判断需要人工介入。
二、变更接口清单
本条无新增、修改、删除任何 HTTP 接口或字段,只涉及错误码返回扩展。所有派单与保险相关接口可能新增 605075 返回。
| # | 接口类别 | 变更类型 | 说明 |
|---|---|---|---|
| - | 派单全量接口 | 错误码新增 | 可能返回 605075(锁租约丢失) |
| - | 保险自动投退保逻辑 | 错误码新增 | 可能返回 605075(锁租约丢失) |
三、错误码定义
605008(LOCK_ACQUIRE_TIMEOUT)——抢锁超时,等待超时专用
IErrorCode.of(605008,
"抢锁超时,系统繁忙,请稍后重试",
"fleet")
发生时机:调用方等待 Redis 分布式锁的时间窗口(默认数秒)内没抢到,因为别的调用方仍持有。
出现位置(仅限"等待没抢到"两处,共用失败工厂 lockFailure.get()):
AssignmentResourceLockManagerline 592(抢锁等待超时)FleetInsuranceLifecycleLockManagerline 142(抢锁等待超时)
⚠️ 这两处是真正 throw 的位置。各业务方法(如 AssignmentService:489/579/1112/1240 等)传进去的是「取不到锁时用哪个异常」的工厂,不是抛出点——按业务方法去数会得到一个大得多的数字。
重试是否有效:✅ 有可能有效(别的持有方释放后重试成功率高)
前端引导:"系统繁忙,请稍后重试"
605075(LOCK_LEASE_LOST)——资源锁已失效,租约丢失专用
IErrorCode.of(605075,
"资源锁已失效({0}),本次操作未提交;请确认是否存在并发操作后重新发起",
"fleet")
占位符 {0}:锁的身份标签,由 FleetLockDiagnostics.labelOf() 生成,格式是 <中文锁名>(<键里的 ID>),例如:
用车需求锁(123)、订单需求项锁(…)、团期配车聚合锁(…)、派车组锁(…)、司机派单锁(456)、车辆派单锁(…)、车牌唯一锁(…)、车架号唯一锁(…)、常驻司机绑定锁(…)、司机保险任务锁(…)。
前缀认不出来时落 未知锁(<原始键>)。多把锁一起失效时用顿号「、」连起来,顺序与加锁顺序一致。
⚠️ 不要按 冒号分隔的英文键 去解析它——那不是它的形态。
发生时机:成功抢到锁,持有期间(执行业务逻辑时)发现租约已不属于自己,意味着 Redis 侧发生过事件(主从切换、键被清、看门狗续期失败、租期到期)。
出现位置(共四处调用 FleetLockDiagnostics.leaseLost()):
AssignmentResourceLockManagerline 386(绑定资源前的续租失败)AssignmentResourceLockManagerline 444(事务提交前的续租失败)AssignmentResourceLockManagerline 499(FOR UPDATE 后归属复核失败,行已锁定但 Redis 键丢失)FleetInsuranceLifecycleLockManagerline 263(持锁期内复核发现键已不属于自己)
重试是否有效:❌ 无效(成因在 Redis 侧,本地重试无法恢复;且期间可能已有并发写入)
前端引导:"数据一致性风险。请刷新后重试,或联系管理员人工核实是否存在并发操作。"
四、契约约束与正确调用方式
错误码使用场景对应关系
所有派单与保险接口的以下场景可能返回这两个码:
- 派单创建/改派/最终确认/撤销取消 → 需抢派单资源锁 → 可能返 605008 或 605075
- 司机保险档案编辑/自动投退保 → 需抢司机保险锁 → 可能返 605008 或 605075
- 共用关系变更(团期派车组共用确认等) → 涉多把锁 → 可能返 605008 或 605075
前端分别处置逻辑(伪代码)
{
"code": 605008,
"message": "抢锁超时,系统繁忙,请稍后重试",
"success": false
}
→ 重试逻辑:可展示"系统繁忙,请稍后重试",3-5 秒后自动重试或提示用户手动重试
{
"code": 605075,
"message": "资源锁已失效(用车需求锁(8xxx)),本次操作未提交;请确认是否存在并发操作后重新发起",
"success": false
}
→ 人工核实逻辑:不推荐直接重试,应该:
- 刷新当前页面查看最新状态
- 若问题仍存在,人工核实是否有并发操作(多个标签页同时操作同一资源)
- 若无并发,联系技术支持并提供锁标签(括号内内容)以排查 Redis 问题
五、边界行为
- 未登录 → 401(网关拦截)
- 资源不存在 → 404 或其他业务错误码(先于锁检验)
- 下游服务降级 → 通常返 200 + 降级结构,不影响锁的判定
- 重要:605008 与 605075 都是锁层面的故障,不是"参数校验失败"也不是"业务规则冲突";前端应按后端差异返回走不同分支,禁止对所有 60508x 统一重试
六、验证情况(请连同它的边界一起看)
部署:PR #8015(44a40de90)与 #8042(8629bf5dd)已合入 dev-v3,且 merge-base --is-ancestor 对测试服 fleet 部署点 51571c58a 均为真。
⚠️ 下表是对 origin/dev-v3 = c1a6e96c8 的逐条代码复核,不是测试服实测读数。 605075 需要「持锁期间租约丢失」这一 Redis 侧条件,无法按需构造,因此没有任何一次真实调用产出过 605075。⇒ 前端在联调时也构造不出这个码,请按文档实现分支、不要等"先见到再写"。
| 检查项 | 结果 |
|---|---|
错误码定义(AssignmentErrorCode) |
605008 保留,605075 新增 ✓ |
| 605075 抛出点(4 处) | AssignmentResourceLockManager:386/444/499、FleetInsuranceLifecycleLockManager:263 ✓ |
| 605008 保留点(2 处) | AssignmentResourceLockManager:592(等待超时)、FleetInsuranceLifecycleLockManager:142(等待超时) ✓ |
占位符 {0} 生成(FleetLockDiagnostics.labelOf()) |
正确生成"需求/派车组/司机/车辆等:ID"格式 ✓ |
| 重入安全性 IT | FleetInsuranceLifecycleLockReentrancyIntegrationTest 5 条(真 Redis + 独立 H2),覆盖跨事务被挡住、REQUIRES_NEW 子事务真取锁、异步线程真取锁;配 assertContentionActuallyHappened 竞争证明 ✓ |
| 分辨力(变异证明) | 去掉 ownerThread 守卫 / 把 RegistryBinding.suspend() 改空体,两轮各只红 1 条且红在竞争证明那半边 ✓ |
网关实测(针对自锁修复本身,非 605075):2026-09-20 经网关对同一关系同一请求做了前后对比,修复后返回业务结果而非 605008(工单 #8002 AC-2 已勾,附实测请求与响应)。
结论的边界:已验证的是「自锁不再发生」与「605008 的语义已收窄到只剩等待超时」;未验证的是「605075 真的会在租约丢失时到达前端」。前端无需新增接口对接,仅需按错误码区分提示文案。
七、不影响范围
- 仅影响:派单与保险相关接口的错误码返回集合扩展(从含 605008 扩展为含 605008 或 605075)
- 零影响:
- 所有接口的请求参数、响应结构、成功路径数据
- 其他错误码(600xxx / 601xxx 等)
- 前端页面布局与非 UI 交互的业务流程(刷新后仍可完成操作)
八、相关历史 PR
| PR | Issue | 说明 | 是否仍有效 |
|---|---|---|---|
| #8015 | #8002 | 保险生命周期锁改造为事务级可重入 | ✅ 最新 |
| #8042 | #8002 | REQUIRES_NEW 子事务复用外层锁 + 605008 拆出 605075 + 修复 dev-v3 编译 | ✅ 最新 |
📌 2026-09-21 订正记录(旧值保留在此,接住按旧值 grep 的人)
本文件首版由自动化生成,下列五处与代码不符,已按 origin/dev-v3 = c1a6e96c8 实测订正:
| 处 | 原值(错) | 订正为 | 怎么查的 |
|---|---|---|---|
| 605008 抛出点行号 | :594 / :145 |
:592 / :142 |
grep -rn 'logAcquireTimeout' hl-fleet-service/src/main/java/ |
| 605075 占位符示例 | requirement:123、driver:456 |
用车需求锁(123)、司机派单锁(456) |
FleetLockDiagnostics:96-117 的 labelOf + LABEL_BY_PREFIX:66-80 |
| 单测描述 | "LEGACY 派单重入恢复" | 删除 | 本单与"LEGACY"无关,该说法在代码与测试里均无对应物 |
| 相关 PR | #8036 挂在 #8002 名下、说是"补漏掉的 import" | 删除该行 | #8036 实为 #7973 的「补两族锁串联的重入 IT 与跨常驻 precheck 断言」 |
| 第六节标题 | "测试环境已验证" | "验证情况(请连同它的边界一起看)" | 该节内容是代码复核表,不是实测读数;按交付指南 §2.1 的口径不得混称 |
十、相关文档
- 关联 Issue: wx/HL#8002
- 关联 PR: wx/HL#8015、wx/HL#8042
- 错误码参考: fleet 派单错误码段 605000-605999 完整列表
关联 / 联系人
链接
联系人
- 后端负责人: @wx