Sqlite 不正常斷電,官網文件說很強,事實上呢?

最近遇到一個工作上的問題,就是客戶在我們的 daemon 不正常關閉 (類似 kill -9) 之後重開,sqlite 常常會出現問題。

由於前人做了不良的示範:Log 印不夠多,因此我們初步只知道以下狀況

  1. 通常中獎的機器先前都發生過不正常斷電
  2. 接著就無法啟動,顯示 sqlite database 錯誤(這裡 log 印不夠不知道是甚麼錯誤)
  3. 我們進去手動使用 sqlite 指令開起來下個 PRAGMA integrity_check; 然後就好了

傑克這實在太神奇了,到底為甚麼下個 check 就會好,這個 database 是發生了甚麼事情?

考慮過以下狀況:

  • database 如果檔案真的爛掉,那麼 integrity check 只會檢查出錯誤而已 -> 應該不是
  • 相關的 lock 被卡住 -> 還是無法解釋 integrity check 之後會好

因為實在太迷了所以一時之間我和 Jack Yu 都沒想到原因,trace code 也沒有發現可疑的部分。

因為很多 user 都遇到這一個問題,因此我嘗試 reproduce 看看有沒有機會做出來,在一串激烈的 kill -9 -> 重啟 -> kill -9 -> 重啟的暴力測試下很快地就做出來了,趕緊掛個 code 進去印出那該死的 log,得到:

sqlite3_exec error: attempt to write a readonly database

喔喔喔喔喔!(howhow語氣),這看起來超級有幫助的。

看了一下我們開 sqlite db 的部分,發現我們是用 sqlite3_open_v2 帶 SQLITE_OPEN_READONLY 開的,也就是我們將 database 開啟成 read-only mode。

問題來了,但我們的 query 就只是一個 select 而已,為甚麼會需要寫入呢?

配合剛剛的事實:這問題總是在斷電時產生,我到 sqlite db 旁一看發現有 sqlite.db-journal 檔產生,直覺告訴我,因為 sqlite 想要將 journal 檔案寫回原本的 database,但沒辦法寫所以哭說他想寫,你不給寫。

google 了一堆關鍵字都沒有人寫出真正的原因,直到我用 “sqlite journal readonly site:www.sqlite.org” 下去找,燈燈燈,真正的原因出現惹,跟我猜的八九不離十呀。

4. Read-Only Databases

至於究竟為甚麼下個 check 就會好,原因是我們用 sqlite 直接進去的話不會開成 read-only mode,因此他就會直接把 journal 合併進去了,一旦合併進去就可以使用 read-only mode 開啟,當然就沒事囉!

雖然我和 Jack Yu 都認為正確的實作應該是開成 read-only mode 的話就不嘗試 roll-back 或在 memory 裡面做 roll-back 硬開,但他沒這樣實作我想也是有他的苦衷吧,和各位分享~~

FYI

有興趣的話可以看一下 sqlite 對於 Database 怎麼會爛掉的解釋

其中有個我覺得很雷的:2.6. Carrying an open database connection across a fork(),你只要開著 sqlite 並 fork 就會爛掉了 G_G

Written by

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store