这类问题我又碰上了一次。OCR 服务处理完发票后回调业务系统,网关超时重试了两次,业务侧收到三次相同回调,同一张发票生成了 3 条应付记录,财务对账时发现金额翻倍。
第一反应是「重试与首次请求落在不同实例,本地缓存无效」,验证下来不成立。
真正麻烦的地方在于,几个原因的现象几乎一样。回调没有携带唯一事件 id 或 id 未被校验;去重表写在业务事务之外,并发下都判为空;业务主键允许重复,没有唯一索引兜底。
先看回调报文里有没有事件 id
python -c "import json;d=json.load(open('cb.json'));print(d.keys())"
接着用事件 id 加唯一索引建去重表
psql -c "create table cb_dedup(event_id text primary key, created_at timestamptz default now());"
随后在插入业务数据前先占位去重键
psql -c "insert into cb_dedup(event_id) values('evt_8821') on conflict do nothing returning event_id;"
等前面都确认过,再比对回调次数与业务记录数
psql -c "select event_id,count(*) from invoice group by 1 having count(*)>1;"
恢复的判定其实很直接。同一事件 id 重复回调只插入一条业务记录,冲突请求返回 200 但不产生副作用,发票表按事件分组计数全部为 1。
去重必须和业务写入在同一个事务里,先查后插的写法在并发下必然漏,唯一索引才是最后一道闸。如果同一现象隔几天又出现,说明根因没找到,别当偶发处理。
