mysql server(mysqld)の起動をn回リトライして待つshellコマンドとmakefileを書いた

# この記事の対象読者

- mysql serverの起動を待つスクリプトが欲しい
- docker-compose で mysqlを使っているが上手く初期化できない
  - なぜか初期化処理がbuildプロセスだけ失敗する

- 以下のエラーでググってhostを直しても一向に直らない

```
ERROR 2013 (HY000): Lost connection to MySQL server at 'reading initial communication packet', system error: 0
```

# TL;DR

```shell
mysql server(mysqld) の起動を待つ処理を入れましょう。
通常であればこれでいいと思います

$ mysqladmin --wait --count 3 ping || exit 1
```

docker-composeだとちょっと癖があるので上記以外で対処します。

shellはこんな感じ

```shell
// 5回繰り返す
for i in 1 2 3 4 5; do mysqladmin ping &>/dev/null && break || sleep 1  && [ 5 -eq $i ] && exit 1; done

// 出来るまで繰り返す
until (mysqladmin ping &>/dev/null) do echo '.' && sleep 1; done

```

人によってはこっちでもいいかも: [処理が成功するまで実行しつづける@katzchang](https://gist.github.com/katzchang/f41ab26b196fbba5f91d59778011e082)

makefile


```make
mysql/lifecheck: 
	until (mysqladmin ping &>/dev/null) do echo '.' && sleep 1; done

mysql/wait:
	which mysqladmin
	@echo "waiting boot mysql..."
	$(MAKE) mysql/lifecheck

```




# mysql server(mysqld)の起動を待つshellコマンドとmakefileを書いた

mysql server(mysqld)で起動直後にサーバ接続ができないケースがあった。


困ったので接続できるまでn回リトライし続けるshellコマンドと makefileを書いた。

**無限に待ちたいわけではない** ので今回は `n回` と言う制約を持たせる。

僕はdocker-composeを使っているのでそれ前提で話をすすめる。

同じく困ってる人はいるんじゃないかな、と思ったのでメモる。

# docker-compose で mysqlコンテナへ起動直後に接続できない!


今回は `docker-compose` でmysqlのコンテナを立てるケースを考える。

コンテナのbuild直後にmysqlに接続できないことがあった。

これでは、buildしたあとにエラーが起きDBの初期化処理が続けてできない。

マイグレーションもできないし、設定もできなくて困る。


# コンテナ起動直後はmysql server(mysqld)が立ち上がっていない。

`docker container` が立ち上がった直後は `mysql server(mysqld)` がまだ起動していない。

`mysql client(mysqladmin)` から接続を試みるとこのエラーが出る。

```
ERROR 2013 (HY000): Lost connection to MySQL server at 'reading initial communication packet', system error: 0
```

エラーメッセージでぐぐるとhostの設定が足りないとの記事が出てきてめちゃめちゃハマった。

そうじゃない。

# sleep n秒が1番簡単な解決方法




```
マイグレーションなどの処理の前に sleep 60 する
```

`mysql server` のデーモン起動(`mysqld`)まで待てばいいのでこれでok。

# 起動までリトライしたい

60秒を超えてmysql serverが起動した場合、 `sleep 60` だと対応できない。
時間 or n回 リトライするような作りになっていればよい。

[How do I write a retry logic in script to keep retrying to run it upto 5 times?](https://unix.stackexchange.com/questions/82598/how-do-i-write-a-retry-logic-in-script-to-keep-retrying-to-run-it-upto-5-times) より拝借


```shell
for i in 1 2 3 4 5; do command && break || sleep 1; done
```

これを使えば `5回, 1秒ごとに 成功するまでcmdを繰り返す` 状態を作り出せる。


mysql serverのlifecheckは `mysqladmin ping` で行える。

```shell 
for i in 1 2 3 4 5; do mysqladmin ping && break || sleep 1 ; done
```


これだと mysql serverが起動してようとしてなろうと次の処理へ行ってしまう。

そこで最終ループのときに `exit 1` で抜けるようにした

```shell

for i in 1 2 3 4 5; do mysqladmin ping && break || sleep 1 && [ 5 -eq $i ] && exit 1; ; done
```

# makefile で rangeを指定する

build中のタスクランナーとしてmakefileを使っている。

n回処理する記述は以下のように書ける。


```make

RETRY_COUNT=30
RETRY_RANGE=$(shell seq 1 $(RETRY_COUNT) | xargs)

mysql/lifecheck: 
	for i in $(RETRY_RANGE); do mysqladmin ping &>/dev/null && break || echo '.' && sleep 1  && [ $(RETRY_COUNT) -eq $$i ] && exit 1; done

mysql/wait:
	which mysqladmin
	@echo "waiting boot mysql..."
	$(MAKE) mysql/lifecheck

```

無限に続ける方はこっち。こっちの方が仕事が明確

```make
mysql/lifecheck: 
	until (mysqladmin ping &>/dev/null) do echo '.' && sleep 1; done

mysql/wait:
	which mysqladmin
	@echo "waiting boot mysql..."
	$(MAKE) mysql/lifecheck

```