VPN 断开后恢复从 SQL Server 到 Oracle 的加载
VPN 断开后,调用方无法确定 Oracle 中的 commit 是否已经落地。持久化的批 ID 让抽取、应用和对账可以逐一得到回答。
VPN 断开时,跨数据库传输就会中断,而“再跑一次”并不是恢复方案。源端抽取可能已经完成,目标端可能已经提交,或者回复只是在返回途中丢失了。这三种状态需要不同的处理,而只知道“出错”的客户端无法区分它们。
解决办法是架构层面的:给每次运行一个持久化的批标识,让数据库来回答问题,而不是让客户端去猜。
数据库到底保证了什么
SQL Server 和 Oracle 都在单个事务内提供原子性。两者都不会跨两个事务提供原子性。连接丢失不会损坏数据库,也不会强制手工回滚,因为服务器会自行了结自己的事务。真正被破坏的是调用方对结果的认知。
分别提交就是分别提交。承载两个连接的 VPN 不会把两次提交合并成一个工作单元。
你从未听说过的那次提交
有一种故障会惩罚天真的重试逻辑:服务器提交了,确认却从未到达调用方。Oracle 将其记录为未知结果问题,并在事务文档中为此提供 Transaction Guard。通信错误并不能证明已提交的事务失败,把它当作证据正是批次被重复投递的原因。这一区别在重试路径上最重要:操作员只有一条错误消息,没有别的信号可依据。
因此恢复步骤必须从目标端读取结果,而不是从错误文本推断。
给传输一个批标识
每次运行都会得到一个不可变的批 ID,目标端记录它用该 ID 做了什么。生命周期分四步,而且无论哪个厂商,看起来都一样:
- 抽取一个有记录的模式和边界的、已识别的不可变源批次。
- 传输到目标的暂存区,并在宣布就绪前发现不完整或冲突的内容。
- 应用在目标自身的唯一性或锁定规则下执行,并在同一本地事务中记录完成。
- 对账通过受支持的协议核对持久结果,然后再确认或重试。
重试会复用同一个 ID。每次尝试都新建 ID,会破坏你刚刚建立的去重机制——这听起来理所当然,直到某个重试包装器自动生成了一个。
这个 ID 也给了你日后可供对账的依据,哪怕客户端进程早已消失。
先认领批次,再应用
应用步骤必须认领批 ID、执行业务变更,并在一个本地事务中记录完成。把检查和插入拆成两条语句,会把你想消除的竞争重新引入。如果两个工作者竞争,恰好一个认领成功,另一个会被告知。
没有完成记录,并不能证明进行中的事务永远不会提交。认领必须把并发的尝试串行化,或者协议必须在做出任何决定前读取持久结果。
这种串行化应使用数据库支持的机制,而不是应用代码里“检查后再重复”的循环。
设计仍然无法承诺的事
暂存让工作可以重启,并减少对单一长连接的依赖。但它不会在 SQL Server、Oracle 以及作业执行的任何外部副作用之间创造单一原子提交。如果业务确实需要,就设计并验证一个受支持的分布式事务或对账流程,包括由谁负责结果未定的工作。任何更弱的做法都属于对账运行手册,而不是传输代码。
要接受三个位置的中断:传输期间、应用期间,以及提交之后但确认之前。然后检查业务结果,而不是客户端消息,让批次账本说明工作是未完成、已提交,还是仍未解决。