问题现象
用户创建了一笔工单并审批通过,可工单迟迟不出现,在已完成、已取消列表里都查不到,像凭空消失。后来重新申请的同类工单却能正常生成。
背景
系统在审批通过后,会通过回调自动为工单建单。为避免同一门店重复建单,建单处加了锁;同一门店下可能有多笔工单在同一批次同时走审批回调。
根本原因
两个约束同时被破坏。一是建单锁只按门店加了粗粒度锁,没区分更细的物料类目,本可并行的建单被同一把锁互斥,这一笔没抢到锁就报了数据处理中请勿重复提交;二是异步回调把这个失败当成功消费掉了,只把审批单标成审批通过但建单失败,消息按成功确认、不再重试,工单于是永远没生成。
解决方案
两处一起改:把锁粒度下沉到真正冲突的维度(门店加物料类目),让不冲突的建单能并行;回调消费遇到业务失败不能按成功确认,要区分可重试失败与终态失败,配好重试或死信兜底。
// 锁粒度下沉:门店 + 任务类型 + 物料类目,不冲突的可并行
String lockKey = stationId + ":" + taskCode + ":" + categoryId;
// 回调消费:失败不能按成功 ack
try {
createOrder(...);
} catch (LockConflictException e) {
throw new RetryableException(e); // 交给重试或死信,不静默吞
}
测试建议
构造同一维度多笔请求在同一批次并发建单的场景,断言抢锁失败的那笔最终能被重试补偿出来,而不是只测单笔顺序执行。专门验证回调消费失败时消息不会被按成功确认,会进入重试或死信。也要覆盖不同物料类目并行建单不应互相阻塞。
经验总结
并发操作的锁粒度要匹配真实的冲突维度,别用粗锁误伤本可并行的请求;异步回调里业务处理失败绝不能按成功确认,要能重试或落死信。