dbd 到 mongo 的序列化问题及稳定性
最近工作压力有点大,情绪偶然会失控,要好好反省一下自己。
上周本来打算写 CSP 相关的东西,但是目前做得还不够多,积累的经验不足,就放弃了。中间找到很好的参考资料,一并放在这里。一篇是比较短的论文,可以观其大略:http://www.cs.ucf.edu/courses/cop4020/sum2009/CSP-hoare.pdf,另一个链接是 hoare 写的关于进程演算的书:https://dl.dropboxusercontent.com/u/5660734/CSPbook_2004_Hoare.rar 对于理解 CSP 这个模型的数学基础很有帮助。不过书好长,还没看完。。。
本周合并了另一个项目组对 mongodb 改造的修改,对方完全重写我的代码,但没有配套单元测试,合并当天就测出 bug 了,有点碎碎念啊。没办法,硬着头皮剥离一部分依赖,独立出一个可执行文件来测 bson_format 的问题。正如之前写的 这篇 博客提到的,难点在于类型系统的对接。本次修改增加了一个 array 迭代,保证初次在引擎这边分配 array 的时候,已经是一块足够长的内存,而不是我原来实现时,迭代 bson,有一个元素就合并一个。另外,上次序列化为 bson 时,我错将 array 当 mapping 来序列化了,原有的测试只针对长度,没针对具体内容,本次修改已经修正。
另外一个地方,是关于内存对象的生命周期的。本次修改直接重写了 dbo_new, dbo_load, dbo_unload 等方法,这些方法控制了 dbd 内存中,存放在 apr_list 里的一系列 db 对象。gamed 通过 rpc 调用,以 dbo_new 或者 dbo_load 方法创建对象后,除非调用 dbo_unload,否则对象是不会被释放的,整个生命周期非常清晰。虽然 dbd 启动过程中初始化了 vm 虚拟机,实际上并没有跑 lpc 脚本,所有工作都是在 c 层实现的。本次修改中,在序列化过程里,用到的字符串局部变量,都是通过 vm 虚拟机来申请和释放的。个人对此持保留看法。在序列化过程中,所用到的 key 值都是临时性的,bson 化后就没有意义。一个简单粗暴的做法,就是直接在函数内定义定长字符串数组,然后每次需要序列化 bson key 的时候,都复制 / 格式化到这个数组上。这一方面可以避免堆区内存分配,另一方面避免无谓的内存泄露,以及可能出现的二次释放问题。
稍后,通过单元测试,修正了当天测试暴露出来的,反序列化 array 嵌套 mapping 对象时,array 元素没有初始化好,导致序列化嵌套 mapping 时,引擎 core dump 的问题。接着,对方又抛出了一个新的修改,依然是没有单元测试。。目标是修正引擎的概率性崩溃,依据是序列化 mapping 过程中,对 key 做字符串化操作时,会改动到原有的 mapping,然后造成内存泄露和 / 或 mapping 元素的二次释放问题,引致引擎崩溃。经单元测试发现,这个理由不成立,但代码里的确会有误释放的问题。至于误释放为什么没有引起单元测试崩溃,这个还需要看看 gc 那一块的代码。这次 bug fix 就没有吸合了,稍后通过在函数内定义一个局部定长字符串数组,加上 key 的复制来解决吧。
通过这几次引擎的修改,越来越发现,没有单元测试的代码,真的会没有信心的。不是说你用这个代码跑了两周,没有发现存储有问题,这个代码就是没有问题的。不同项目组里,用到的储存结构不一样,也许你的代码嵌套 3 层以后就会崩掉呢?或者就是这次暴露出的,复合类型的嵌套储存 bug 呢?没有办法用单元测试重现的 bug,只靠看代码猜测,我觉得是不靠谱的,因为修好后你也没办法重现 bug 的环境,看看本次修改是否有效。希望大伙能够早日认同这个道理
做完底层又要赶逻辑代码,差点回不了家,残念