30 ポイント 投稿者 darjeeling 20 일 전 | 4件のコメント | WhatsAppで共有

ultrathink.art は、AIエージェントが自律的に運営するEコマースのショッピングサイトだ。商品デザイン、注文処理、ブログ執筆まで、すべてをAIが担当している。この文章は、そのショッピングサイトを実際に Stripe 決済まで処理する本番環境で SQLite により運用しながら経験したことをまとめたものだ。


構成: ファイル4個、ボリューム1個

本番環境では primary(注文・商品・ユーザー)、cache(Rails キャッシュ)、queue(バックグラウンドジョブ)、cable(Action Cable)の計4つの SQLite データベースを運用しており、すべて1つの Docker ボリュームに保存される。

Rails 8 が SQLite を第一の選択肢にしてくれたおかげで、実際にデプロイの単純化、コネクションプール管理の不要化、別個の DB サーバー不要といった利点を享受できた。


WAL モードが並行性を可能にする仕組み

SQLite の基本ジャーナルモードは、書き込み時に DB 全体をロックするため、同時リクエストの多い Web アプリには不向きだ。WAL(Write-Ahead Logging)モードでは、書き込みは別の -wal ファイルに追記され、読み取りはメインファイルを引き続き使うため、多数の読み取りと単一の書き込みを同時に行える。Rails 8 は SQLite で WAL モードをデフォルトで有効化する。


事故: 注文2件が消えた

2月4日、2時間のあいだに 11 回のコミットを main に push した。各 push ごとに Kamal のブルーグリーンデプロイが実行され、既存コンテナと新規コンテナが同時に同じ WAL ファイルを開く重複区間が生じた。11 回のデプロイが重なったことで、コンテナ A のドレイン中に B が起動し、B が完全に準備できる前に C のデプロイが始まる状況が発生した。

注文 16 番と 17 番は Stripe で決済が成功し、顧客口座からも金額が引き落とされていたが、DB にはレコードが残らなかった。sqlite_sequence で確認すると、自動増分カウンターは 17 を指していたのに、実際の行は 15 件しかなかった。


解決策: デプロイ速度を落とせ

解決策は技術的なものではなく、手続き上のものだった。関連する変更をまとめてデプロイし、短時間の連続 push を避けるルールを、AI エージェントたちが従うガバナンスファイル(CLAUDE.md)に明記した。

これは SQLite の問題ではなく、デプロイパイプラインの問題だ。PostgreSQL は TCP ソケット経由で接続するため、新しいコンテナも同じ DB サーバーに接続し、書き込み順序は DB エンジンが管理する。SQLite は共有 Docker ボリュームのファイルシステムロックに依存しているが、コンテナが重なるとこれが壊れる。


sqlite_sequence: フォレンジックツールとして活用する

sqlite_sequence テーブルは、SQLite で最も過小評価されているデバッグツールだ。あとから削除された行であっても、過去に自動増分値が割り当てられた最大値を記憶している。現在の行数とシーケンス値の差が想定外に開いていれば、何らかの行を誤って削除したというシグナルになる。


誰も教えてくれない落とし穴

PostgreSQL 開発者が癖で使う ILIKE は、SQLite では構文エラーになる。代わりに LOWER(name) LIKE を使う必要がある。json_extract は、値が数値として保存されていれば整数を返すため、文字列比較時に静かに失敗する。kamal app exec は毎回新しいコンテナを生成するが、2GB RAM のサーバーで同時に 2 回実行すると、OOM killer が Web プロセスを殺してしまう。


もう一度選んでも SQLite を使うか?

そうだ。単一サーバーで適度な書き込み負荷であれば、SQLite はインフラの複雑さを丸ごと取り除いてくれる。バックアップも sqlite3 .backup コマンドひとつで十分だ(WAL モードと同時書き込みも安全に扱える)。水平スケールや真のマルチライター並行性が必要になる日が来たら、そのとき PostgreSQL にマイグレーションすればよい。Rails はその移行を簡単にしてくれる。


原文: ultrathink.art Blog, 2026.04.03

4件のコメント

 
click 18 일 전

インフラの複雑さの問題があるにしても、ショッピングモールのように同時書き込みが多く必要なところでは、最初から sqlite を使わないほうが良い選択ではないでしょうか?
しかも Docker で構成したものだったなら、postgresql 構成のインフラ複雑度もそれほど高くはないはずです。
rails で作っていて、エコシステムが sqlite を使う方向に枠組みができているので、それで良いと誘導されたのではないかという印象です。
注文漏れのような重大なエラーが発生したのに、pg のような同時書き込みに強い DB を使うべきところであっても、ショッピングモールのように同時書き込みが多く必要な場所では、最初から sqlite を使わないほうが良い選択ではないでしょうか?
rails で作っていて、エコシステムが sqlite を使う方向に枠組み化されているので、それで良いと誘導されたのではないかという気がします。
注文漏れのような重大なエラーが発生しており、これを根本的に解決するのは pg のような同時書き込み DB を使う方法なのに、
sqlite を技術的に好むからといってこれを使い続けると主張するのは、私にはエンジニアとしての信頼を下げる発言に聞こえます。
不要なのに k8s を載せて replica 1 の HPA を構成し、うまく使っていたモノリスを MSA に変える履歴書駆動開発の逆バージョンのように感じます。

 
okxrr 18 일 전

コンテナで立ち上げるときにボリューム設定を誤ったのが問題なのであって、同時書き込みができないことが問題になったわけではありません。記事をもう一度読めば、同時書き込みができない点は busy timeout で十分カバーできると書かれています。ボリューム設定は
ipc=host で解決できるはずです。

Postgres は結局運用が必要ですが、SQLite はどこでもアプリのバイナリさえ配布すれば済みます。

数多くの運用経験と失敗が積み重なった結果、SQLite が流行し始めているのであって、
同時書き込みは記事でもまったく問題ではないとはっきり書かれています。

 
darjeeling 18 일 전

実際のところ、いろいろ設定すればよいだけなのですが、結局は経験が必要なんですよね(笑)。とにかく、私はこういう文章が好きです。

 
darjeeling 18 일 전

そうですね。だからこそ、こういう記事がたまに上がってくるといいですね。