Dockerのローカル開発環境でCakePHP3を使った開発をしていると、たまに
General error: 5 database is locked
と表示されてエラー画面が返されることがある。今回はその対処法と原因について紹介する。検証したCakePHPのバージョンは3.6.13。
原因はSQLiteのデータベースロックだった
CakePHP3には、ローカル開発環境でデバッグが容易になるDebug Kitという機能がある。画面右下にCakePHPのケーキマークが出てくるアレのことだ。このDebug Kitは設定を何もいじらないと、SQLiteを使って直近のリクエスト情報を貯めておく性質がある。
1 2 3 4 5 6 7 8 9 10 11 |
$ ls -la total 1292192 drwxrwxrwx 10 hoge staff 320 3 1 12:34 . drwxr-xr-x 26 hoge staff 832 11 14 17:41 .. -rwxr-xr-x 1 hoge staff 0 1 17 21:32 .gitkeep drwxrwxrwx 6 hoge staff 192 11 14 11:34 cache -rw-r--r-- 1 hoge staff 503083008 3 1 12:34 debug_kit.sqlite -rw-r--r-- 1 hoge staff 33536 3 1 12:34 debug_kit.sqlite-journal drwxrwxrwx 2 hoge staff 64 12 4 2018 sessions drwxrwxrwx 2 hoge staff 64 12 4 2018 tests $ |
↑はtmp配下のディレクトリ構成をls -laしたものだ。debug_kit.sqliteというDBファイルがあるのが分かると思う。
General error: 5 database is lockedのエラーメッセージが出た時は上記DBファイルに加えて、debug_kit.sqlite-journalというファイルが生成されている。今回の原因を解消するにはこの2ファイルをmvコマンドで名前を変えるか、あるいは思い切ってrmコマンドで削除してしまえばOK。
参考)https://github.com/cakephp/cakephp/issues/7517
debug_kit.sqliteファイルの役割
debug_kit.sqliteファイルはリクエスト情報を格納しておく簡易DBの役割を果たしている。もしローカル開発環境下でsqlite3コマンドが出来るのであればぜひ下記を試してみてほしい:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
$ sqlite3 debug_kit.sqlite SQLite version 3.24.0 2018-06-04 14:10:15 Enter ".help" for usage hints. sqlite> .schema CREATE TABLE IF NOT EXISTS "requests" ( "id" CHAR(36) NOT NULL, "url" TEXT NOT NULL, "content_type" VARCHAR, "status_code" INTEGER, "method" VARCHAR, "requested_at" DATETIME NOT NULL, CONSTRAINT "primary" PRIMARY KEY ("id") ); CREATE TABLE IF NOT EXISTS "panels" ( "id" CHAR(36), "request_id" CHAR(36) NOT NULL, "panel" VARCHAR, "title" VARCHAR, "element" VARCHAR, "summary" VARCHAR, "content" BLOB(4294967295), CONSTRAINT "primary" PRIMARY KEY ("id"), CONSTRAINT "unique_panel" UNIQUE ("request_id", "panel"), CONSTRAINT "request_id_fk" FOREIGN KEY ("request_id") REFERENCES "requests" ("id") ON UPDATE RESTRICT ON DELETE RESTRICT ); sqlite> |
↑こんな感じで、SQLiteの内部構成を確認することができる。
SQLiteのjournalファイルとは
SQLiteはMySQLや他のRDBMSと異なり、暗黙のトランザクションとオートコミットを実装している。MySQLでトランザクションをスタート/コミットさせるには明示的にコマンドを打つ必要があるが、SQLiteは勝手にそれをやってくれるわけだ。
その性質から、SQLiteに何らかの書き込み処理が発生する時はその書き込み情報を一時的に格納しておくためのjournalファイルが生成される。要は、
journalファイルが存在していると、DBへデータを書き込んでいる最中である
ことを意味している。下記参考サイトで詳細が解説されているので、合わせて参照いただければ理解が深まると思う。
参考)https://www.antun.net/tips/api/sqlite.html
ローカル開発環境のCakePHP3でSQLiteがロックされる原因
CakePHP3のDebug KitがSQLiteにリクエスト情報を書き込む時もjournalファイルが生成されるのだが、たとえば同一ページを瞬間的に何度もリロードしたり別々のページを同時に開こうとする時に今回の問題が発生しやすい。
SQLiteのDBロックがいつまでも解除されずにリクエストの処理待ち状態が続き、最終的にphp.iniで設定されているMaximum execution timeを超えた時にGeneral error: 5 database is lockedのエラーメッセージが出てしまう
ことが起こりうるわけだ。
僕の経験上、MacにVagrantを使ったローカル開発環境を構築して開発していた時はGeneral error: 5 database is lockedのエラーに出くわすことは殆どなかったが、Dockerを使ったローカル開発環境に切り替えてからは頻繁にこのエラーに遭遇するようになった感触がある。原因はおそらく、Docker for Macのvolumeマウントの遅さ。volumeをマウントする際にcachedやdelegatedの設定を入れれば、多少なり解消する可能性はある。
あるいは、SQLiteにリクエスト情報を格納せずにMySQLへリクエスト情報を格納させるという手もありかもしれない。CakePHP3では、Debug Kitにリクエスト情報の格納先を変更させる設定を書くことができる。