2

In my spring-boot project I'm using out-of-the-box integration for Flyway (org.flywaydb.flyway-core) and have some migration scripts which are executed on startup and managed via default flyway_schema_history-table.

The project also uses a module bringing its own flyway migration scripts, which are migrated programmatically and keeping track of migrations in an other moduleX_schema_history-table.

As the migrations of the main-project needs to work on some of the tables created via the modules migration, the module-migration needs to happen before the flyway-plugin migrates the main-projects scripts.

How can I achieve executing modules migration programmatically and before the main-apllications fly-integration kicks in? How and when is the flyway-plugin migration triggered?

//EDIT: I tried to execute code before FlywayAutoConfiguration via a @Configuration class annotated with

@AutoConfigureBefore({FlywayAutoConfiguration.class})
@AutoConfigureAfter({DataSourceAutoConfiguration.class})

but unfortunatly the class is still instanciated after FlywayAutoConfiguration.

//EDIT: I asked the related (more general) question how to order AutoConfiguration from modules, too How to use @AutoConfigureOrder in a spring boot configuration class from a module - Stack Overflow

kai-dj
  • 315
  • 1
  • 18

2 Answers2

2

Flyway migration will kick-in during startup if you configured it correctly in your application yaml/properies file.

In order to start another flyway migration before the main app migration you can use flyway command line or create an separate custom_pom.xml project file for another module and trigger it via mvn plugin from command line: mvn flyway:migrate before executing the main app.

Dmitriy
  • 515
  • 7
  • 14
  • flyway migration of the main app does kick in on startup, but how, when/were is it triggered? unfortunatly command line or maven are not the desired option - the modules migration must happen programatically and before the ootb-pluging does its work. – kai-dj Jul 17 '20 at 11:58
  • 1
    When you add flyway dependency and flyway entries in your application config, springboot will try to autoconfigure flyway instance and run it during application startup (https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.html). If you want to have more granulated control on how it is initialized and started go with programmatic initialization: https://flywaydb.org/getstarted/firststeps/api – Dmitriy Jul 17 '20 at 12:23
  • thanks. helpful to see, were flyway-core does its magic. But is there no way to trigger code-execution before FlywayAutoConfiguration is loaded by spring autoconfiguration? Like a beforeFlywayAutoConfigurationHook ^^ – kai-dj Jul 17 '20 at 12:57
  • You can disable FlywayAutoConfiguration by spring and do the migration by yourself. Do you want that? – Abhinaba Chakraborty Jul 17 '20 at 15:17
  • no, the FlywayAutoConfiguration-triggered migration should still be done, but only after the other migration has happened programmatically. – kai-dj Jul 19 '20 at 21:09
0

I had a similar question about how to programmatically trigger a Flyway migration and figured out how to do it. The provided FlywayMigrationInitializer runs the Flyway migrations at bean creation time by default, which is what we're trying to prevent. To get around this, we can override the FlywayMigrationInitializer's afterPropertiesSet method to do nothing, and then call Flyway.migrate() from the desired location in your code. Your impl of the FlywayMigrationInitializer must be declared as a Bean.

Why is this useful? You might need to run migrations when:

  • at startup only if a certain argument is passed to the application on startup (my use case), or if a certain env variable is set
  • an endpoint is called
  • on receiving a Kafka event
  • at a scheduled time

How I did it:

Add the dependency to build.gradle (similar for Maven pom.xml):

    implementation 'org.flywaydb:flyway-core'

application.yaml:

spring:
  flyway:
    enabled: true 
    baseline-version: 0 # Needed so you can run a migration that starts with 'V1__'
  ...

FlywayConfig:

@Configuration
public class FlywayConfig {

    @Bean
    public FlywayMigrationInitializer flywayInitializer(Flyway flyway,
                                                        ObjectProvider<FlywayMigrationStrategy> migrationStrategy) {
        return new MyFlywayMigrationInitializer(flyway, migrationStrategy.getIfAvailable());
    }
}

MyFlywayMigrationInitializer:

public class MyFlywayMigrationInitializer extends FlywayMigrationInitializer {

    public MyFlywayMigrationInitializer(Flyway flyway,
                                            FlywayMigrationStrategy migrationStrategy) {
        super(flyway, migrationStrategy);
    }

    public void afterPropertiesSet() throws Exception {
        // no-op.  We don't want to run the migrations at startup
        log.debug("Not running Flyway migrations at application startup");
    }

MigrationRunner:

public class MigrationRunner {

    public void runMigrations() {
        MigrateResult result = null;
        try {
            result = flyway.migrate(); // runs the migrations
        } catch (FlywayException e) {
            if (e.getMessage().contains("no schema history table")) { // need to run baseline first to set up the table
                flyway.baseline(); // then try to run the migrations again
                result = flyway.migrate();
            } else {
                log.error(e.getMessage(), e);
            }
        }
        // optionally do something with the result
    }
}
Mark
  • 4,970
  • 5
  • 42
  • 66