Java-based flyway migrations scripts are applied to all schemas
See original GitHub issueDescribe the bug
In this blog post the flyway team describes the patterns that you can use for schema-based multi-tenancy. We tried to use the pattern with a single database, multiple schema with multiple histories. The migration table for each schema is stored in the schema itself. According to Quarkus guides this case is supported (in the quarkus-flyway guide)
For example, we have the following two schemas:
- “base” schema with its history defined from:
- *.sql migration scripts in resources locations “database/base” (full project path “src/main/resources/database/base”)
- Java migration scripts in source package “database/base” (full project path “src/main/java/database/base”)
- “mycompany” schema with its history defined from:
- *.sql migration scripts in resources locations “database/mycompany” (full project path “src/main/resources/database/mycompany”)
- Java migration scripts in source package “database/mycompany” (full project path “src/main/java/database/mycompany”)
So let’s say that we have the following scripts in our project:
- For the “base” history:
- database/base/V1__FirstBaseMigration.sql (located in resources of the project)
- database/base/V2__SecondBaseMigration.java (located in java sources of the project)
- For the “mycompany” history:
- database/mycompany/V1__FirstMycompanyMigration.sql (located in resources of the project)
- database/mycompany/V2__SecondMycompanyMigration.sql
The above setup is almost the same as the quarkus-quickstarts project “hibernate-orm-multi-tenancy-quickstart” (if you enable “database” profile) but it just includes both sql and java migrations (the quickstart contains only *.sql) and also instead of using two different databases (and their default schema) we are using a single database with two different schemas (“base” and “mycompany”) by pointing the two datasources to the same database and by setting the config options “quarkus.flyway.TENANT_ID.schemas” to the correct schema for each tenant id in application.properties
:
quarkus.datasource.base.db-kind=postgresql
quarkus.datasource.base.jdbc.url=jdbc:postgresql://localhost:5432/quarkus_test
quarkus.flyway.base.schemas=base
quarkus.flyway.base.locations=classpath:database/base
quarkus.flyway.base.migrate-at-start=true
quarkus.datasource.mycompany.db-kind=postgresql
quarkus.datasource.mycompany.jdbc.url=jdbc:postgresql://localhost:5432/quarkus_test
quarkus.flyway.mycompany.schemas=mycompany
quarkus.flyway.mycompany.locations=classpath:database/mycompany
quarkus.flyway.mycompany.migrate-at-start=true
Expected behavior
I assume that the correct behavior is for the above two histories to applied correctly to their corresponding schema of the database (“base” history for “base” schema and the “mycompany” history for “mycompany” schema).
Actual behavior
If we restrict our two histories to use only .sql migration scripts then we get the expected behavior. But if we include at least one Java migration script to any of the two histories then the quarkus-flyway will try to apply this Java migration to both schema. For example in the above example case the quarkus-flyway will try to apply the “database/base/V2__SecondBaseMigration.java” migration script to both schema “base” and “mycompany”. Of-course because there is also a “V2__SecondMycompanyMigration.sql” migration script for the “mycompany” schema the quarkus-flyway will throw the following exception: Failed to start application: org.flywaydb.core.api.FlywayException: Found more than one migration with version 2 Offenders: -> ~/quarkus-quickstarts/hibernate-orm-multi-tenancy-quickstart/target/classes/ (JDBC) -> ~/quarkus-quickstarts/hibernate-orm-multi-tenancy-quickstart/target/classes/database/mycompany/V2__SecondMycompanyMigration.sql (SQL)
In the above case quarkus-flyway throws exception because there is also an sql migration with the same version for “mycompany” history but there are cases in which there is no conflict between versions and the Java migration of the “base” history could be wrongly applied to all schemas. I think that quarkus-flyway-deployment scans all classes that implement JavaMigration interface in the project and it does not filter the classes based on the package prefix for each tenant schema (it will include all JavaMigrations for each schema migration).
How to Reproduce?
Steps to reproduce:
- Go to quarkus-quickstarts project “hibernate-orm-multi-tenancy-quickstart” (the “database” profile)
- Set the two datasources to point to the same database and use different schemas by setting “quarkus.flyway.base.schemas=base” and “quarkus.flyway.mycompany.schemas=mycompany” in application.properties. Or you can also use the two databases setup with the default schema.
- Set the locations for flyway “quarkus.flyway.base.locations=classpath:database/base” and “quarkus.flyway.mycompany.locations=classpath:database/mycompany” in application.properties
- Create a Java migration script in the “database/base” java package and an sql script with the same version number in the “database/mycompany” resources.
- Run in dev mode
I have attached a patch file quarkus-schema-multi-tenancy.txt with the above steps for quarkus-quickstarts project “hibernate-orm-multi-tenancy-quickstart” (I removed “database” profile in the patch)
Output of uname -a
or ver
Linux 5.11.0-38-generic 20.04 Ubuntu x86_64
Output of java -version
OpenJDK Runtime Environment Temurin-17+35 (build 17+35)
GraalVM version (if different from Java)
No response
Quarkus version or git rev
“2.3.1.Final” or “2.4.1.Final”
Build tool (ie. output of mvnw --version
or gradlew --version
)
Apache Maven 3.8.1
Additional information
No response
Issue Analytics
- State:
- Created 2 years ago
- Comments:5 (2 by maintainers)
My understanding of the context is a little sketchy, but I’ll try and put together a fix and put up a PR
Thanks for the analysis.
As you seem to have got pretty far, mind providing a Pull Request?