数据导入的过程也应该是一个“事务Transaction”

上上周导入数据,我们约定了数据格式,写好了导入脚本,进行了充分的测试。但是当天给我们的数据结构却发生了巨大的变化,但是给我们的更新window就是当天,所以我们只能再写程序将格式转化为我们先前约定的格式。我和 @nasiless 同学结对做这件事。经过3、4个小时的努力,转换没问题了,将中间数据转化的操作演练了一下,没啥问题。当准备好停机升级,又从数据提供方得知其中一部分数据是错误的,然后又给我我们一份补丁文件(名-值对,需要覆盖现有值)。我们由于担心重新生成数据非常费时间,所以分析一下补丁对我们操作的影响。@nasiless 同学和我确认应该只影响其中一个中间数据文件,然后写脚本修正了一下。最后我们“成功”导入数据恢复服务了,当时很庆幸居然成功了(那个时候已经是晚上11点了)。当然其中有一部分导入失败的数据(9xx条)我们输出了一份文件准备后面手工输入。

一周后来(期间系统又进来很多新数据),我们发现导入的数据中失败的那部分可能是由于补丁文件也需要应用在另一部分的中间文件上造成的。可是当我们想反推这个过程并重现它的时候,我们发现我们的临时转换脚本(就是把那天修改的数据文件向中间文件转换的程序)居然放在了我的/tmp目录下,而其间我的mac重启过,所以这些脚本已经不见了。剩下的只有那个导入失败的log和映射补丁文件,我们经过推演问题已经变得非常复杂了。几个错误因素结合在一起,数据再回复的确是个非常费脑子的工作。还好经过反复排查,这些结果都是可逆的,不过其中人工操作众多,非常费事。我们也只能硬着头皮做了。因为这个系统还涉及到钱的问题,钱又涉及分帐问题,所以反推非常麻烦。

我们的结果还算好的,因为有方法反退效果。可是最大的问题在于这个过程本身应该是“事务”,必须全部完成或者全部恢复,当时我们的做法没有遵循这个基本的原则。而且我们的导入程序中复用了系统逻辑代码,但是忘记修改事务边界,没有将多个事务join起来,所以造成了脏数据的出现。这个也是后来折腾我们的一个主要原因。

所以教训就是:数据导入操作本身就应该使用事务,确定好所有的事务边界。而且数据导入的流程也应该是一个事务,不应该允许可能出现脏数据的请款存在,因为这种问题往往是追悔莫及的,也就是safe steps的极致。