问题背景
最近在做pika的新存储引擎BlobKV开发,所谓的BlobKV就是将key和value分开存储,目的是解决大value情况下,rocksdb compaction导致的写放大问题。由于新的存储引擎和旧的存储引擎在数据格式上的不兼容,所以需要将旧的存储引擎数据通过命令的形式迁移到新的存储引擎上。
问题现象
迁移工具比较简单,流程就是先在源端执行Bgsave,生成数据库的备份dump,然后开启五个线程对五种数据类型的数据库扫描key并且将key分发到多个迁移线程中,由迁移线程发送給对端。
问题是,在数据量较大时,每次都是迁移到一定的数据量,迁移程序就会自动退出,并且没有打印任何退出前的日志:-(
问题定位
- 程序非正常退出,首先想到的是看有没有生成coredump文件,查看了下,没有生成coredump文件。
- 然后查看系统的dmesg,发现一个迁移线程退出的日志,分配内存失败,原因是free内存太少,于是释放了系统的页缓存。重新迁移,发现还是会自动退出,但demsg中已经没有错误日志了,所以也排除了OOM杀死进程。
- 继续查看系统的messages日志,执行cat /var/log/messages,发现也是没有任何信息
- 再看迁移程序中的日志,也没有发现任何ERROR日志(迁移程序非正常退出时候都会打印ERROR日志)
- 程序就这样死的一点遗言都没有,很是绝望。但仔细想想,既然程序不是主动退出,那肯定是被系统杀死的,系统怎么杀死进程呢?--发信号量啊。于是抱着最后一根救命稻草,我在程序里捕获了所有信号量(31个系统经常发的信号)。
终于,凶手抓住了!! 系统給迁移进程发了一个SIGPIPE信号,该信号的意思是“管道破损,没有读端的管道写数据”,也就是说对端将迁移线程的连接給断开了,此时发送线程在写一个不可读的管道,导致程序退出....
问题解决
- 迁移程序忽略SIGPIPE信号
- 将对端连接超时断开的时间从60s改成了600s
- 迁移线程发送失败后,尝试重新连接,而不是直接退出
问题思考
- 对端为什么会将迁移线程的连接断开,按理说迁移线程一直在发送数据,不可能是在非活跃状态。(需要进一步调查)
- 在写socket程序时记得要忽略SIGPIPE信号量
网友评论