アプリ起動時にDB起動を待つ

公開:2020/09/18
更新:2020/09/18
1 min読了の目安(約800字TECH技術記事

めったに変更されないけれども参照回数が多いデータは、アプリケーションのメモリ内にキャッシュするのが効率的です。
これはISUCONでもよくやる最適化なのですが、起動時にキャッシュするデータをDBから読み込むと再起動試験でfailする要因になりえます。

  • db.Open() はコネクションプールを初期化するだけでDBに接続しないので接続エラーも起こらないのだが、このAPIの結果 err == nil だと接続できるように錯覚しがち
  • 開発中のアプリケーション再起動時に常にDBサーバーが生きているのでエラーが起こらない
  • 再起動試験しても先にDBサーバーが起動したり、アプリサーバーの再起動が完了(再起動前のDBからキャッシュを取得できる)してからDBを再起動し始めるとやはりエラーが起こらず気づけ無い。

こんなミスでfailノースコア敗退するのはあまりにももったいないので、アプリケーション起動時にDBの起動を待つコードスニペットなりコピペライブラリなりを用意しておきましょう。

	// db.Open() が成功した直後にこれを入れる.
	for {
		err := db.Ping()
		if err == nil {
			break
		}
		log.Print(err)
		time.Sleep(time.Second * 2)
	}
	log.Print("DB ready!")

db.Ping() はコネクションを1つ作り、ドライバが対応している場合はそのDBに対してPingをします。接続エラーが起こる場合はこれで検出できます。
Ping の代わりに _, err := db.Exec("SELECT COUNT(*) FROM table") などで使うテーブルへのアクセスできることの確認&バッファプールに乗せるなどの工夫も考えられます。