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

Ensky Lin
5 min readApr 17, 2018

--

最近遇到一個工作上的問題,就是客戶在我們的 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

No SQLite database (regardless of whether or not it is WAL mode) is readable if it is located on read-only media and it requires recovery. So, for example, if an application crashes and leaves an SQLite database with a hot journal, that database cannot be opened unless the opening process has write privilege on the database file, the directory containing the database file, and the hot journal. This is because the incomplete transaction left over from the crash must be rolled back prior to reading the database and that rollback cannot occur without write permission on all files and the directory containing them.

至於究竟為甚麼下個 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

--

--