问题现象
一份历史合同重新签署已完成,主数据里合同编号和履约起止日期都换成了新的,但管理后台该对象的履约期仍显示重签前的旧日期,看起来像新合同没生效。运营据此无法确认续签结果,误以为合同还没签完。
背景
后台页面不直接读合同主表,而是读一份合同快照来渲染履约期。正常重签应在更新主表的同时删除旧快照、按新合同重建快照。系统里存在两条重签处理路径,走哪条由一个区域灰度开关决定。
根本原因
这次重签命中了旧路径:回调只更新了合同主表(新合同号、新履约期),没有触发快照重建。于是主表指向的那份快照仍是重签前的旧快照(旧合同号、旧履约起始日期),页面读到的就是这份没刷新的旧数据,导致展示与主表长期不一致。走旧路径的触发条件是该对象所在区域未开启快照重建灰度,回调因此降级到只改主表的分支——坑不在少写一行,而在旧路径这条分支本身就不做快照。
// 问题版:两条路径分叉,旧路径压根不含快照重建
void onResignCallback(Contract c) {
if (grayEnabled(c.getRegion())) {
newPath(c); // 新路径:更新主表 + 重建快照
} else {
// 旧路径:只更新主表,从设计上就漏了快照
contractRepo.updateMain(c);
}
}
解决方案
临时止血:补触发一次合同状态变更回调,让重签重新走新路径,更新主表并删除旧快照、按新合同重建快照,完成后校验快照的合同号与履约起始日期已更新、主表快照引用指向新快照,并在后台复核履约期恢复为新合同日期。若补触发后快照仍未重建,说明该区域灰度未开,需先为该区域开启快照重建灰度再重跑。 长期根治:把两条路径收口到同一步骤,更新主表后必须同步重建快照,并放在同一事务里避免只成功一半;再加一层定时对账,主表与快照关键字段不一致就补重建并告警,别让同步只依赖记得调用。
// 修复版:不管走哪条路径,都收口到主表+快照同一原子操作
@Transactional
void onResignCallback(Contract c) {
contractRepo.updateMain(c); // 更新主数据
snapshotService.rebuild(c); // 同一事务内重建快照,避免只成功一半
}
// 兜底:定时对账,主表与快照关键字段不一致就补重建/告警
void reconcileSnapshot() {
for (Contract c : findMainSnapshotMismatch()) {
snapshotService.rebuild(c);
alert("snapshot mismatch, rebuilt", c.getId());
}
}
测试建议
构造区域未开启快照重建灰度下的重签场景:重签完成后既查主表也查快照,断言两者的合同号与履约起止日期一致,并校验后台展示的履约期等于新合同日期。补充灰度开/关两条路径的对比用例,确保任一路径完成后主表与快照都同步;再覆盖重签后立即读页面的时序,防止快照晚于主表刷新造成的短时不一致。
经验总结
写在主表、读在快照时,更新入口有几条快照重建就得跟几条;漏掉任何一条旁路,页面就会一直停在旧数据上。