Configure run/repair db migrations

Refs: #1517
This commit is contained in:
eikek
2022-05-22 00:07:36 +02:00
parent 79688c7711
commit 3764f9265b
14 changed files with 116 additions and 36 deletions

View File

@ -0,0 +1,17 @@
/*
* Copyright 2020 Eike K. & Contributors
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
package docspell.store
case class SchemaMigrateConfig(
runMainMigrations: Boolean,
runFixupMigrations: Boolean,
repairSchema: Boolean
)
object SchemaMigrateConfig {
val defaults = SchemaMigrateConfig(true, true, false)
}

View File

@ -42,6 +42,7 @@ object Store {
def create[F[_]: Async](
jdbc: JdbcConfig,
schemaCfg: SchemaMigrateConfig,
fileRepoConfig: FileRepositoryConfig,
connectEC: ExecutionContext
): Resource[F, Store[F]] = {
@ -58,7 +59,7 @@ object Store {
}
xa = HikariTransactor(ds, connectEC)
fr = FileRepository.apply(xa, ds, fileRepoConfig, true)
st = new StoreImpl[F](fr, jdbc, ds, xa)
st = new StoreImpl[F](fr, jdbc, schemaCfg, ds, xa)
_ <- Resource.eval(st.migrate)
} yield st
}

View File

@ -13,9 +13,9 @@ import cats.effect.Async
import cats.implicits._
import cats.~>
import docspell.store._
import docspell.store.file.{FileRepository, FileRepositoryConfig}
import docspell.store.migrate.FlywayMigrate
import docspell.store.{AddResult, JdbcConfig, Store}
import doobie._
import doobie.implicits._
@ -23,6 +23,7 @@ import doobie.implicits._
final class StoreImpl[F[_]: Async](
val fileRepo: FileRepository[F],
jdbc: JdbcConfig,
schemaCfg: SchemaMigrateConfig,
ds: DataSource,
val transactor: Transactor[F]
) extends Store[F] {
@ -38,7 +39,7 @@ final class StoreImpl[F[_]: Async](
FunctionK.lift(transact)
def migrate: F[Int] =
FlywayMigrate[F](jdbc, xa).run.map(_.migrationsExecuted)
FlywayMigrate[F](jdbc, schemaCfg, xa).run.map(_.migrationsExecuted)
def transact[A](prg: ConnectionIO[A]): F[A] =
prg.transact(xa)

View File

@ -10,15 +10,19 @@ import cats.data.OptionT
import cats.effect.Sync
import cats.implicits._
import docspell.store.JdbcConfig
import docspell.store.migrate.FlywayMigrate.MigrationKind
import docspell.store.{JdbcConfig, SchemaMigrateConfig}
import doobie.implicits._
import doobie.util.transactor.Transactor
import org.flywaydb.core.Flyway
import org.flywaydb.core.api.output.MigrateResult
class FlywayMigrate[F[_]: Sync](jdbc: JdbcConfig, xa: Transactor[F]) {
class FlywayMigrate[F[_]: Sync](
jdbc: JdbcConfig,
cfg: SchemaMigrateConfig,
xa: Transactor[F]
) {
private[this] val logger = docspell.logging.getLogger[F]
private def createLocations(folder: String) =
@ -49,28 +53,46 @@ class FlywayMigrate[F[_]: Sync](jdbc: JdbcConfig, xa: Transactor[F]) {
def run: F[MigrateResult] =
for {
_ <- runFixups
fw <- createFlyway(MigrationKind.Main)
_ <- logger.info(s"!!! Running main migrations")
result <- Sync[F].blocking(fw.migrate())
result <- runMain
} yield result
def runMain: F[MigrateResult] =
if (!cfg.runMainMigrations)
logger
.info("Running main migrations is disabled!")
.as(new MigrateResult("", "", ""))
else
for {
fw <- createFlyway(MigrationKind.Main)
_ <- logger.info(s"!!! Running main migrations (repair=${cfg.repairSchema})")
_ <- if (cfg.repairSchema) Sync[F].blocking(fw.repair()).void else ().pure[F]
result <- Sync[F].blocking(fw.migrate())
} yield result
// A hack to fix already published migrations
def runFixups: F[Unit] =
isSchemaEmpty.flatMap {
case true =>
().pure[F]
case false =>
(for {
current <- OptionT(getSchemaVersion)
_ <- OptionT
.fromOption[F](versionComponents(current))
.filter(v => v._1 >= 1 && v._2 >= 32)
fw <- OptionT.liftF(createFlyway(MigrationKind.Fixups))
_ <- OptionT.liftF(logger.info(s"!!! Running fixup migrations"))
_ <- OptionT.liftF(Sync[F].blocking(fw.migrate()))
} yield ())
.getOrElseF(logger.info(s"Fixup migrations not applied."))
}
if (!cfg.runFixupMigrations) logger.info(s"Running fixup migrations is disabled!")
else
isSchemaEmpty.flatMap {
case true =>
().pure[F]
case false =>
(for {
current <- OptionT(getSchemaVersion)
_ <- OptionT
.fromOption[F](versionComponents(current))
.filter(v => v._1 >= 1 && v._2 >= 32)
fw <- OptionT.liftF(createFlyway(MigrationKind.Fixups))
_ <- OptionT.liftF(
logger.info(s"!!! Running fixup migrations (repair=${cfg.repairSchema})")
)
_ <-
if (cfg.repairSchema) OptionT.liftF(Sync[F].blocking(fw.repair()).void)
else OptionT.pure[F](())
_ <- OptionT.liftF(Sync[F].blocking(fw.migrate()))
} yield ())
.getOrElseF(logger.info(s"Fixup migrations not applied."))
}
private def isSchemaEmpty: F[Boolean] =
sql"select count(1) from flyway_schema_history"
@ -95,8 +117,12 @@ class FlywayMigrate[F[_]: Sync](jdbc: JdbcConfig, xa: Transactor[F]) {
}
object FlywayMigrate {
def apply[F[_]: Sync](jdbcConfig: JdbcConfig, xa: Transactor[F]): FlywayMigrate[F] =
new FlywayMigrate[F](jdbcConfig, xa)
def apply[F[_]: Sync](
jdbcConfig: JdbcConfig,
schemaCfg: SchemaMigrateConfig,
xa: Transactor[F]
): FlywayMigrate[F] =
new FlywayMigrate[F](jdbcConfig, schemaCfg, xa)
sealed trait MigrationKind {
def table: String

View File

@ -22,13 +22,15 @@ import org.mariadb.jdbc.MariaDbDataSource
import org.postgresql.ds.PGConnectionPoolDataSource
trait StoreFixture extends CatsEffectFunFixtures { self: CatsEffectSuite =>
def schemaMigrateConfig =
StoreFixture.schemaMigrateConfig
val xa = ResourceFixture {
val cfg = StoreFixture.memoryDB("test")
for {
ds <- StoreFixture.dataSource(cfg)
xa <- StoreFixture.makeXA(ds)
_ <- Resource.eval(FlywayMigrate[IO](cfg, xa).run)
_ <- Resource.eval(FlywayMigrate[IO](cfg, schemaMigrateConfig, xa).run)
} yield xa
}
@ -42,6 +44,7 @@ trait StoreFixture extends CatsEffectFunFixtures { self: CatsEffectSuite =>
}
object StoreFixture {
val schemaMigrateConfig = SchemaMigrateConfig.defaults
def memoryDB(dbname: String): JdbcConfig =
JdbcConfig(
@ -94,7 +97,7 @@ object StoreFixture {
xa <- makeXA(ds)
cfg = FileRepositoryConfig.Database(64 * 1024)
fr = FileRepository[IO](xa, ds, cfg, true)
store = new StoreImpl[IO](fr, jdbc, ds, xa)
store = new StoreImpl[IO](fr, jdbc, schemaMigrateConfig, ds, xa)
_ <- Resource.eval(store.migrate)
} yield store

View File

@ -10,7 +10,7 @@ import cats.effect.IO
import cats.effect.unsafe.implicits._
import docspell.logging.TestLoggingConfig
import docspell.store.StoreFixture
import docspell.store.{SchemaMigrateConfig, StoreFixture}
import munit.FunSuite
@ -21,7 +21,7 @@ class H2MigrateTest extends FunSuite with TestLoggingConfig {
val ds = StoreFixture.dataSource(jdbc)
val result =
ds.flatMap(StoreFixture.makeXA).use { xa =>
FlywayMigrate[IO](jdbc, xa).run
FlywayMigrate[IO](jdbc, SchemaMigrateConfig.defaults, xa).run
}
assert(result.unsafeRunSync().migrationsExecuted > 0)
@ -40,7 +40,7 @@ class H2MigrateTest extends FunSuite with TestLoggingConfig {
val result =
ds.flatMap(StoreFixture.makeXA).use { xa =>
FlywayMigrate[IO](jdbc, xa).run
FlywayMigrate[IO](jdbc, SchemaMigrateConfig.defaults, xa).run
}
result.unsafeRunSync()

View File

@ -11,7 +11,7 @@ import cats.effect.unsafe.implicits._
import docspell.common.LenientUri
import docspell.logging.TestLoggingConfig
import docspell.store.{JdbcConfig, StoreFixture}
import docspell.store.{JdbcConfig, SchemaMigrateConfig, StoreFixture}
import com.dimafeng.testcontainers.MariaDBContainer
import com.dimafeng.testcontainers.munit.TestContainerForAll
@ -32,7 +32,7 @@ class MariaDbMigrateTest
JdbcConfig(LenientUri.unsafe(cnt.jdbcUrl), cnt.dbUsername, cnt.dbPassword)
val ds = StoreFixture.dataSource(jdbc)
val result = ds.flatMap(StoreFixture.makeXA).use { xa =>
FlywayMigrate[IO](jdbc, xa).run
FlywayMigrate[IO](jdbc, SchemaMigrateConfig.defaults, xa).run
}
assert(result.unsafeRunSync().migrationsExecuted > 0)
// a second time to apply fixup migrations

View File

@ -11,7 +11,7 @@ import cats.effect.unsafe.implicits._
import docspell.common.LenientUri
import docspell.logging.TestLoggingConfig
import docspell.store.{JdbcConfig, StoreFixture}
import docspell.store.{JdbcConfig, SchemaMigrateConfig, StoreFixture}
import com.dimafeng.testcontainers.PostgreSQLContainer
import com.dimafeng.testcontainers.munit.TestContainerForAll
@ -34,7 +34,7 @@ class PostgresqlMigrateTest
val ds = StoreFixture.dataSource(jdbc)
val result =
ds.flatMap(StoreFixture.makeXA).use { xa =>
FlywayMigrate[IO](jdbc, xa).run
FlywayMigrate[IO](jdbc, SchemaMigrateConfig.defaults, xa).run
}
assert(result.unsafeRunSync().migrationsExecuted > 0)