quarkus: Hibernate reactive + Flyway extension causes UnsatisfiedResolutionException

Describe the bug Simply adding the quarkus-flyway extension when using Hibernate Reactive causes:

Caused by: javax.enterprise.inject.UnsatisfiedResolutionException: Unsatisfied dependency for type javax.persistence.EntityManagerFactory and qualifiers [@Default]
	- java member: io.quarkus.hibernate.reactive.runtime.ReactiveSessionFactoryProducer#emf
	- declared on CLASS bean [types=[io.quarkus.hibernate.reactive.runtime.ReactiveSessionFactoryProducer, java.lang.Object], qualifiers=[@Default, @Any], target=io.quarkus.hibernate.reactive.runtime.ReactiveSessionFactoryProducer]
	at io.quarkus.arc.processor.Beans.resolveInjectionPoint(Beans.java:487)
	at io.quarkus.arc.processor.BeanInfo.init(BeanInfo.java:362)
	at io.quarkus.arc.processor.BeanDeployment.init(BeanDeployment.java:226)

Expected behavior Flyway should be usable in an application that uses the hibernate-reactive extension.

Actual behavior Will not start.

To Reproduce Steps to reproduce the behavior:

  1. Take hibernate-reactive-quickstart
  2. Add the following dependencies:
        <!-- Flyway specific dependencies -->
        <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-flyway</artifactId>
        </dependency>

        <!-- JDBC driver dependencies -->
        <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-jdbc-postgresql</artifactId>
        </dependency>
  1. mvn quarkus:dev

Environment (please complete the following information):

  • Output of java -version: openjdk version “11.0.7” 2020-04-14
  • Quarkus version or git rev: 1.6.0.Final

Additional context (Add any other context about the problem here.)

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 11
  • Comments: 60 (34 by maintainers)

Most upvoted comments

@andreas-eberle My applications.properties file looks like this:

quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=removed
quarkus.datasource.password=removed
quarkus.datasource.jdbc=false
quarkus.datasource.reactive.url=postgresql://127.0.0.1:5432/mydb

The quarkus.datasource.jdbc=false line is very important. If it’s missing, Quarkus won’t boot.

not working with native build

2021-09-09 15:22:10,892 INFO [org.hib.rea.pro.imp.ReactiveIntegrator] (JPA Startup Thread: default-reactive) HR000001: Hibernate Reactive Preview 2021-09-09 15:22:10,896 ERROR [io.qua.run.Application] (main) Failed to start application (with profile prod): java.lang.IllegalStateException: 'org.flywaydb.core.internal.scanner.Scanner' is never used in Quarkus

Hi @markusdlugi , I don’t disagree with you about this issue being important, and appreciate the honest feedback.

Still, I can’t work on this now, and wouldn’t suggest someone else to do so either: we’re “full-steam” ahead in making Hibernate ORM v6 happen: when that’s done this will unlock huge benefits for everyone, and will also require to make significant changes to the Hibernate Reactive code base and the Quarkus extensions, including the Envers exension, the Hibernate Search one, and all other 100+ Quarkus extensions depending on these.

To keep the overall workload somewhat achieavable with the small team, we need to execute in this particular order - I hope you appreciate that.

Not least to steer and guide the various teams into the right directions, some of us have POC and drafts exploring potential impact and need for changes in the integration areas, so starting a significant refactoring now in the extension structure of how the Hibernate ORM and Hibernate Reactive ones are related is bound to create huge conflicts - can’t afford that.

I know it’s not ideal, but I’m fine in conceding that Hibernate Reactive adoption can slow down a little - such needs of a more comprehensive integration are the reason for which it’s still marked as experimental: great to have feedback from advanced users like you, but to achieve successfull mass adoption we need to execute on all these points, not just rush Flyway integration.

Until recently it didn’t even have documentation nor support for Oracle; just a couple months ago it wasn’t able to perform DDL operations… priorities 😃 Hopefully you’re seeing the progress.

Thanks!

Yes this is fairly high on my priority list 😃 Breaking it down in multiple smaller tasks, and I’m getting some help.

@Sanne, @DavideD, @geoand

I wanted to bring up some additional points to further stress the importance of this issue. It’s not just about Flyway, Quartz or whatever other extension might also depend on a JDBC datasource. These extensions not working when Hibernate Reactive is active is just a side-effect of a more severe problem in my opinion. The primary problem surfacing in this issue is that you currently don’t have the possibility to run Hibernate ORM (i.e. Blocking) and Hibernate Reactive side-by-side.

However, running Blocking and Reactive next to each other is one of the main value propositions of Quarkus (or more specifically RESTeasy Reactive). It’s even a feature that RESTeasy Reactive will automatically choose whether to run Blocking or Non-Blocking. At the same time, Hibernate Reactive officially doesn’t support running on a worker thread, and will even print errors if you try to run it from anything other than an eventloop thread. You can try to work around those by disabling the HR assertions, but as mentioned, I guess it’s not officially supported and the way it’s intended to be used.

So that means that effectively, if you want to use Hibernate Reactive only for some parts of your application, that doesn’t currently work - because HR doesn’t support @Blocking while the issue at hand also prevents Hibernate ORM to work properly when HR is active. So in the end, at the moment it’s kind of an all-or-nothing situation, where you either have to use HR for the entirety of your application or not at all. Which contradicts the previously mentioned value proposition of only using Reactive in some parts of your applications while sticking to Imperative in others.

I understand that you’re busy with all kinds of stuff both in HR and ORM, and of course the work required to make this run smoothly is not trivial. But if you want people to actually use HR in anything more than sample applications, then this issue should really be prioritized.

Sorry for the long comment. I just wanted to share some of my experiences since we’re actually running some applications with HR in production already, and while most of the time it’s running smoothly, this issue has been bothering us for a long time now.

@andreas-eberle My applications.properties file looks like this:

quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=removed
quarkus.datasource.password=removed
quarkus.datasource.jdbc=false
quarkus.datasource.reactive.url=postgresql://127.0.0.1:5432/mydb

The quarkus.datasource.jdbc=false line is very important. If it’s missing, Quarkus won’t boot.

Any update on this one ? This is really an annoying problem to work with Quarkus reactive

We also hit this issue recently and came up with relative clean solution. We follow the approach from above, but also make sure everything works nicely in dev mode and production.

  • Configure Flyway with the datasource properties provided by Quarkus and replace the reactive url with the jdbc url for Flyway
  • Optionally inject the Quarkus Scheduler so it can be paused during migration, this way you avoid startup issues with scheduled jobs
  • Optionally clean the schema with Flyway, useful for dev mode
  • Finally validate the applied schema with Hibernate, by retrieving the SchemaManager from the SessionFactory, useful for prod mode where you want to make sure the schema is actually usable by Hibernate on startup
@Startup
public class FlywayMigration {

    FlywayMigration(Scheduler scheduler,
                    FlywayConfig flywayConfig,
                    SessionFactory sessionFactory,
                    @ConfigProperty(name = "quarkus.datasource.reactive.url") String datasourceUrl,
                    @ConfigProperty(name = "quarkus.datasource.username") String datasourceUsername,
                    @ConfigProperty(name = "quarkus.datasource.password") String datasourcePassword) {

        Flyway flyway = Flyway
                .configure()
                .dataSource(datasourceUrl.replace("vertx-reactive:", "jdbc:"), datasourceUsername, datasourcePassword)
                .cleanDisabled(!flywayConfig.clean())
                .load();

        if (flywayConfig.migrateAtStart()) {
            scheduler.pause();
            if (flywayConfig.cleanAtStart()) {
                flyway.clean();
            }
            flyway.migrate();
            sessionFactory.getSchemaManager().validateMappedObjects();
            scheduler.resume();
        }
    }

    @ConfigMapping(prefix = "application.database.flyway")
    public interface FlywayConfig {

        boolean migrateAtStart();

        @WithDefault("false")
        boolean cleanAtStart();
    }
}

For this you need the following extensions:

<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-flyway</artifactId>
</dependency>
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-hibernate-reactive</artifactId>
</dependency>
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>

<!-- Optional -->
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-scheduler</artifactId>
</dependency>

We have the following config values set:

application.database.flyway.migrate-at-start=true
quarkus.hibernate-orm.database.generation=none

## Explicitly turns off JDBC and Flyway
quarkus.datasource.jdbc=false
quarkus.flyway.enabled=false

And in dev mode we set application.database.flyway.clean-at-start=true to get back the drop-and-create experience from Hibernate in dev mode.

Hello, In 3.0 seems I have a slightly different error for the same use case: 2023-05-03 11:51:18,447 INFO [io.qua.dep.dev.IsolatedDevModeMain] (main) Attempting to start live reload endpoint to recover from previous Quarkus startup failure 2023-05-03 11:51:18,954 ERROR [io.qua.dep.dev.IsolatedDevModeMain] (main) Failed to start quarkus: java.lang.RuntimeException: io.quarkus.builder.BuildException: Build failure: Build failed due to errors [error]: Build step io.quarkus.arc.deployment.SyntheticBeansProcessor#initRuntime threw an exception: java.lang.IllegalStateException: A synthetic bean with identifier 9c40b3d5cbb6ee6bb4cf2cb7bab0a1fda6694fe5 is already registered: SYNTHETIC bean [types=[org.hibernate.SessionFactory, jakarta.persistence.EntityManagerFactory, java.lang.Object], qualifiers=[@jakarta.enterprise.inject.Default, @Any], target=n/a] at io.quarkus.arc.processor.BeanDeployment.addSyntheticBean(BeanDeployment.java:1370) at io.quarkus.arc.processor.BeanDeployment$BeanRegistrationContextImpl.accept(BeanDeployment.java:1610) at io.quarkus.arc.processor.BeanDeployment$BeanRegistrationContextImpl.accept(BeanDeployment.java:1593) at io.quarkus.arc.processor.BeanConfigurator.done(BeanConfigurator.java:95) at io.quarkus.arc.deployment.SyntheticBeansProcessor.configureSyntheticBean(SyntheticBeansProcessor.java:92) at io.quarkus.arc.deployment.SyntheticBeansProcessor.initRuntime(SyntheticBeansProcessor.java:56) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at io.quarkus.deployment.ExtensionLoader$3.execute(ExtensionLoader.java:909) at io.quarkus.builder.BuildContext.run(BuildContext.java:282)

@Sanne , the exception seems not the same, do you think it should be handled in the same issue ?

Seems it involve different components and some assumptions should be reviewed for the hibernate reactive part. Do you plan to work on this soon ? or do you recommend to workaround this for the moment ?

Thanks

Thanks for your help on this one!

Keeping Flyway in a separate application could work indeed. I have a different kind of solution which seems to work fine in JVM mode (not so sure about native mode but I don’t need it for now):

@ApplicationScoped
public class FlywayWorkaround {

    @ConfigProperty(name = "quarkus.datasource.reactive.url")
    String datasourceUrl;

    @ConfigProperty(name = "quarkus.datasource.username")
    String datasourceUsername;

    @ConfigProperty(name = "quarkus.datasource.password")
    String datasourcePassword;

    public void runFlywayMigration(@Observes StartupEvent event) {
        Flyway flyway = Flyway.configure().dataSource("jdbc:" + datasourceUrl, datasourceUsername, datasourcePassword).load();
        flyway.migrate();
    }
}

I wish I could keep quarkus-flyway in the project, but I need to focus on quarkus-hibernate-reactive for now so I’ll probably stick with that workaround.

Hi @Sanne, thanks a lot for taking the time to explain the situation, that definitely helps. Much appreciated!

The solution provided in the thread to set datasource.jdbc: false works but then in the dev ui, there is no option to generate the initial migration script, is it happening with me only or others are also facing the same issue.

@seeseemelk Yes, it is a custom config. I’ve written notes in codes.

I dont’ get it. How to you reference the migration files? 🤔

This is what I use in my application: https://gist.github.com/seeseemelk/27c601ad8ce5a6a89d423a476d6e9237

@andreas-eberle My applications.properties file looks like this:

quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=removed
quarkus.datasource.password=removed
quarkus.datasource.jdbc=false
quarkus.datasource.reactive.url=postgresql://127.0.0.1:5432/mydb

The quarkus.datasource.jdbc=false line is very important. If it’s missing, Quarkus won’t boot.

not working with native build

2021-09-09 15:22:10,892 INFO [org.hib.rea.pro.imp.ReactiveIntegrator] (JPA Startup Thread: default-reactive) HR000001: Hibernate Reactive Preview 2021-09-09 15:22:10,896 ERROR [io.qua.run.Application] (main) Failed to start application (with profile prod): java.lang.IllegalStateException: 'org.flywaydb.core.internal.scanner.Scanner' is never used in Quarkus

@minhdtb1983 You can change codes like this:

@ApplicationScoped
public class RunFlyway {

    private static final Logger LOGGER = Logger.getLogger(RunFlyway.class);

    @ConfigProperty(name = "quarkus.datasource.reactive.url")
    String datasourceUrl;
    @ConfigProperty(name = "quarkus.datasource.username")
    String datasourceUsername;
    @ConfigProperty(name = "quarkus.datasource.password")
    String datasourcePassword;
    // add a config to get files, we also need add `quarkus.native.resources.includes:db/migration/*.sql` to application.properties
    // example: db/migration/V1.0.1__InitDB.sql
    @ConfigProperty(name = "rds.flyway.native.files")
    List<String> files;

    public void runFlywayMigration(@Observes StartupEvent event) {
        LOGGER.info("int flyway ...");
        QuarkusPathLocationScanner.setApplicationMigrationFiles(files);
        DataSource ds = Flyway.configure().dataSource("jdbc:" + datasourceUrl, datasourceUsername, datasourcePassword).getDataSource();
        FlywayContainerProducer flywayProducer = Arc.container().instance(FlywayContainerProducer.class).get();
        FlywayContainer flywayContainer = flywayProducer.createFlyway(ds, "<default>", true, true);
        Flyway flyway = flywayContainer.getFlyway();
        flyway.migrate();
    }
}

I am considering reusing the existing code of io.quarkus.flyway to the greatest extent, using the data source built by flyway by default, and considering some processing of native mode.

@Sanne Any progress on this issue? Feels pretty odd coming from Spring and experiencing such issues from the very beginning 😭

@gwenneg. Thanks a lot!! We would have never thought of that!