Docker composeのMySQLとの接続でハマった

GoでAPIサーバーを実装するために、Docker composeを使ってMySQLサーバと接続させようとした。

db, err := sql.Open("mysql", dsn)
if err != nil {
  return nil, err
}
defer db.Close()
if err = db.Ping(); err != nil {
  log.Fatal(err)
}

db.Ping()でとりあえず接続を確認すると、必ず失敗してGoアプリのコンテナが終了してしまった。

コンテナ間の接続に時間がかかったりするのが原因ぽかった。

func connect(db *sql.DB) error {
	const max_retries = 5
	retries := 0

	for {
		err := db.Ping()
		if err != nil {
			if retries > max_retries {
				return fmt.Errorf("connection with DB failed: %v", err)
			}

			waitTime := (2 << retries) + rand.Intn(1000)/1000
			time.Sleep(time.Duration(waitTime) * time.Second)

			retries++
			continue
		}
		break
	}
	return nil
}

connect()関数を作って、何回かdb.Ping()をリトライしたら接続できるようになった。

エラーが起こった時に、直ちに失敗として返すのではなく、何回か試すの大事。

ちなみにこの方法はエクスポネンシャルバックオフアルゴリズムっていうらしい。

指数関数的に待機時間を伸ばしていくことで、無駄な待ち時間を減らすことができる。

なるほどなあ。