ExposedでDBの起動を待ってからDatabase.connectする
docker-composeで抱き合わせるときに使えるスニペット.
TL;DR
接続先のデータベース(今回はMariaDB)は環境に応じて書き換えてください.
private const val INTERVAL = 1_000 private const val TRIALS = 5 fun initialize(host: String, database: String, user: String, password: String) { val url = "jdbc:mariadb://$host/$database" fun getConnectionOrNull(): Connection? = try { val properties = Properties().apply { put("user", user) put("password", password) } DriverManager.getConnection(url, properties) } catch (ignored: SQLException) { null } fun tryConnectBlocking(trials: Int): Connection? = (0 until trials).asSequence() .map { getConnectionOrNull() ?: Thread.sleep(INTERVAL) } .filterIsInstance<Connection>() .firstOrNull() Database.connect(getNewConnection = { tryConnectBlocking(TRIALS) ?: throw IllegalAccessException("Couldn't connect to database!") }) }
動機
Ktor + Exposedにてdocker-composeを用いてMariaDBと同時に立ち上げることがあり, その時に先走ってExposedのDatabase.connect
が呼ばれてクラッシュするのを避けたかった次第.
公式によるとアプリケーション側で制御しろとのことだったので*1, それに従い上のようなコードを書いた.
tryConnectBlocking
で接続までのブロッキングを行っている訳だが, map
の返り値の型がConnection
とUnit
の直和型ではなく
Any
型となってしまう所に若干の気持ち悪さが無いといえば嘘になる.*2
*1:https://docs.docker.com/compose/startup-order/
*2:そう思ってしまうのは良いとも良くないとも思う