From 0508529c239bf7f6942b5bf2f0b506ca2dca46eb Mon Sep 17 00:00:00 2001 From: Manjunath Shivanna Date: Tue, 7 Apr 2026 16:06:56 -0400 Subject: [PATCH] Add retry logic to RunMigrations for DB connection failures RunMigrations.apply now accepts optional `retries` (default 30) and `retryDelay` (default 3s) parameters. On failure, it retries using a tail-recursive loop with a configurable delay, allowing downstream apps to survive transient DB connection issues at startup without changes to their call sites. Co-Authored-By: Claude Sonnet 4.6 --- .../libats/slick/db/BootMigrations.scala | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/libats-slick/src/main/scala/com/advancedtelematic/libats/slick/db/BootMigrations.scala b/libats-slick/src/main/scala/com/advancedtelematic/libats/slick/db/BootMigrations.scala index c22bcffc..87d889dc 100644 --- a/libats-slick/src/main/scala/com/advancedtelematic/libats/slick/db/BootMigrations.scala +++ b/libats-slick/src/main/scala/com/advancedtelematic/libats/slick/db/BootMigrations.scala @@ -14,6 +14,7 @@ import scala.jdk.CollectionConverters._ import scala.concurrent.duration.Duration import scala.concurrent.{Await, Future} import scala.util.{Failure, Success, Try} +import scala.annotation.tailrec protected [db] object RunMigrations { private lazy val _log = LoggerFactory.getLogger(this.getClass) @@ -29,15 +30,24 @@ protected [db] object RunMigrations { true } - def apply(dbconfig: Config): Try[Int] = Try { - _log.info("Running migrations") - - val f = flyway(dbconfig) - - val count = f.migrate() - _log.info(s"Ran ${count.migrationsExecuted} migrations") - - count.migrationsExecuted + def apply(dbconfig: Config, retries: Int = 30, retryDelay: Duration = Duration(3, "seconds")): Try[Int] = { + @tailrec + def attempt(remaining: Int): Try[Int] = { + val result = Try { + _log.info("Running migrations") + val count = flyway(dbconfig).migrate() + _log.info(s"Ran ${count.migrationsExecuted} migrations") + count.migrationsExecuted + } + result match { + case Failure(ex) if remaining > 0 => + _log.warn(s"Migration failed, retrying in ${retryDelay.toSeconds}s ($remaining retries left): ${ex.getMessage}") + Thread.sleep(retryDelay.toMillis) + attempt(remaining - 1) + case other => other + } + } + attempt(retries) } private def flyway(dbConfig: Config): Flyway = {