問題背景
最近在做pika的新存儲引擎BlobKV開發(fā),所謂的BlobKV就是將key和value分開存儲,目的是解決大value情況下,rocksdb compaction導(dǎo)致的寫放大問題。由于新的存儲引擎和舊的存儲引擎在數(shù)據(jù)格式上的不兼容,所以需要將舊的存儲引擎數(shù)據(jù)通過命令的形式遷移到新的存儲引擎上。
問題現(xiàn)象
遷移工具比較簡單,流程就是先在源端執(zhí)行Bgsave,生成數(shù)據(jù)庫的備份dump,然后開啟五個線程對五種數(shù)據(jù)類型的數(shù)據(jù)庫掃描key并且將key分發(fā)到多個遷移線程中,由遷移線程發(fā)送給對端。
問題是,在數(shù)據(jù)量較大時,每次都是遷移到一定的數(shù)據(jù)量,遷移程序就會自動退出,并且沒有打印任何退出前的日志:-(
問題定位
- 程序非正常退出,首先想到的是看有沒有生成coredump文件,查看了下,沒有生成coredump文件。
- 然后查看系統(tǒng)的dmesg,發(fā)現(xiàn)一個遷移線程退出的日志,分配內(nèi)存失敗,原因是free內(nèi)存太少,于是釋放了系統(tǒng)的頁緩存。重新遷移,發(fā)現(xiàn)還是會自動退出,但demsg中已經(jīng)沒有錯誤日志了,所以也排除了OOM殺死進程。
- 繼續(xù)查看系統(tǒng)的messages日志,執(zhí)行cat /var/log/messages,發(fā)現(xiàn)也是沒有任何信息
- 再看遷移程序中的日志,也沒有發(fā)現(xiàn)任何ERROR日志(遷移程序非正常退出時候都會打印ERROR日志)
- 程序就這樣死的一點遺言都沒有,很是絕望。但仔細(xì)想想,既然程序不是主動退出,那肯定是被系統(tǒng)殺死的,系統(tǒng)怎么殺死進程呢?--發(fā)信號量啊。于是抱著最后一根救命稻草,我在程序里捕獲了所有信號量(31個系統(tǒng)經(jīng)常發(fā)的信號)。
終于,兇手抓住了??! 系統(tǒng)給遷移進程發(fā)了一個SIGPIPE信號,該信號的意思是“管道破損,沒有讀端的管道寫數(shù)據(jù)”,也就是說對端將遷移線程的連接給斷開了,此時發(fā)送線程在寫一個不可讀的管道,導(dǎo)致程序退出....
問題解決
- 遷移程序忽略SIGPIPE信號
- 將對端連接超時斷開的時間從60s改成了600s
- 遷移線程發(fā)送失敗后,嘗試重新連接,而不是直接退出
問題思考
- 對端為什么會將遷移線程的連接斷開,按理說遷移線程一直在發(fā)送數(shù)據(jù),不可能是在非活躍狀態(tài)。(需要進一步調(diào)查)
- 在寫socket程序時記得要忽略SIGPIPE信號量