今天下午有同事反馈订单列表页状态与详情页状态不一致。
背景:
用户下单及付款等操作后,部分订单信息会推送到公司二方团队用于列表页展示。
(列表页由平台统一维护,详情页业务方维护)
有业务事件发生时,会推一次订单。因而同一订单会多次推送。
不同业务事件间隔不定,存在并发推送一条数据的场景。
核对日志,发现了问题。
订单推送时,平台会以version的大小作为是否抛弃该次推送结果。
背景:
version计算逻辑:用当前时间减去下单时间
将历史推送的version存于DB,每次从DB中获取当前的version(只查有效的,不含软删)。
version+1后,先将当前的version软删,再写进一条数据。
(超过Integer.MAX_VALUE有单独逻辑)
若version不存在,则会根据当前时间重新生成一条version,写入DB。
坑点:平台接收推送数据成功,数据格式正确,即会告知业务方推送成功。
对推送version小于历史version的数据,平台内部会抛弃该次推送的数据,但是仍然告知业务方推送成功。
根据订单号去查该订单的推送version记录:
可以看出有一条version超大。从日志上看,35和10973两条记录的推送间隔在ms级别。
由于平台的策略,导致version10973之后的推送数据均会失效。
根据DB中version数据反推,时序如下:
这里还存在代码bug:之前是向从库查询version记录,在A将34置为无效并同步到从库后,B读从库发现无记录,然后B会向主库写了一条新的version记录10973。读从库增大了A、B之间的时间差。
发现了问题之后,fix策略:
查询version均走主库
若不存在version,生成后写入;
若存在,将当前version+1,带乐观锁version更新(放弃软删+新增步骤);
若更新失败,则查询实时的version,+1后再次带乐观锁version更新;
对于推送的数据部分,实时查主库后再推送;
(实际fix逻辑比这复杂,由于一些原因不过多描述)
事后反思,存在以下问题:
关键操作,不能读从库;
对于同一条数据的操作,不能先软删,再新增一条数据(非原子操作,会造成很多问题,如软删遗漏、新增失败,均会导致问题);
平台应明确告知会抛弃当前数据(告知原因更佳),而不是告知成功;
推送前应实时查最新数据,而是已有数据;