Closed burmanm closed 1 year ago
Could you be sure that it will work under native as well?
@cdmikechen Yes, I did make native image and this workaround works there too. Thanks to you and @seeseemelk .
@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.
Looks like with new versions coming up, it does not work. My quarkus version is 2.13.2.Final
.
With above configurations mentioned , it throws following error
org.flywaydb.core.api.FlywayException: No database found to handle vertx-reactive:mysql://localhost:3306/myapp
is there a specific version I should stick to ?
Thanks
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
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.
@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.
The fix was released in 3.5, but I still need to do the workaround described above . Actually, to have flyway working in a native image, I need to implement the following code:
@Startup
public class FlywayMigration {
FlywayMigration(Scheduler scheduler,
SessionFactory sessionFactory,
FlywayConfig config) {
final FluentConfiguration configuration = Flyway
.configure()
.dataSource("jdbc:".concat(config.getDatasourceUrl()), config.getDatasourceUsername(), config.getDatasourcePassword())
.locations(config.getLocations())
.validateMigrationNaming(config.getValidateMigrationNaming())
.baselineOnMigrate(config.getBaselineOnMigrate())
.baselineVersion(config.getBaselineVersion())
.baselineDescription(config.getBaselineDescription())
.connectRetries(config.getConnectRetries())
.schemas(config.getSchemas())
.table(config.getTable());
if (System.getProperty("org.graalvm.nativeimage.imagecode") != null) {
configuration.resourceProvider(new GraalVMResourceProvider(configuration.getLocations()));
configuration.javaMigrationClassProvider(new GraalVMClassProvider());
}
final Flyway flyway = configuration.load();
if (Boolean.TRUE.equals(config.getMigrateAtStart())) {
scheduler.pause();
if (config.getCleanAtStart()) {
flyway.clean();
}
flyway.migrate();
sessionFactory.getSchemaManager().validateMappedObjects();
scheduler.resume();
}
}
}
public class GraalVMResourceProvider implements ResourceProvider {
private final Location[] locations;
public GraalVMResourceProvider(Location[] locations) {
this.locations = Arrays.copyOf(locations, locations.length);
}
@Override
public LoadableResource getResource(String name) {
if (getClassLoader().getResource(name) == null) {
return null;
}
return new ClassPathResource(null, name, getClassLoader(), StandardCharsets.UTF_8);
}
@Override
public Collection<LoadableResource> getResources(String prefix, String[] suffixes) {
try (FileSystem fileSystem = FileSystems.newFileSystem(URI.create("resource:/"), Map.of())) {
final List<LoadableResource> result = new ArrayList<>();
for (Location location : locations) {
final Path path = fileSystem.getPath(location.getPath());
try (Stream<Path> files = Files.walk(path)) {
files.filter(Files::isRegularFile)
.filter(file -> file.getFileName().toString().startsWith(prefix))
.filter(file -> hasSuffix(file.getFileName().toString(), suffixes))
.map(file -> (LoadableResource) new ClassPathResource(null,
file.toString(), getClassLoader(), StandardCharsets.UTF_8))
.forEach(result::add);
}
}
// Sort DB migration files
final List<LoadableResource> resources = result.stream()
.sorted().collect(Collectors.toList());
return resources;
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
private boolean hasSuffix(String input, String[] suffixes) {
for (String suffix : suffixes) {
if (input.endsWith(suffix) || input.toUpperCase().endsWith(suffix.toUpperCase())) {
return true;
}
}
return false;
}
private static ClassLoader getClassLoader() {
return GraalVMResourceProvider.class.getClassLoader();
}
}
public class GraalVMClassProvider implements ClassProvider<JavaMigration> {
@Override
public Collection<Class<? extends JavaMigration>> getClasses() {
return Collections.emptySet();
}
}
Do I need to have the workaround described? What properties should I have to have Hibernate reactive and flyway working at the same time?
@miguelborges99 We have a (simple) integration test that works with this configuration.
Several things to take care about:
Flyway
objects yourself. See the guide for the recommended way of using Flyway.If you still encounter problems, can you please open another issue with a reproducer? Thank you.
Hi @yrodiere , "Hibernate Reactive can only use the default datasource", does it mean that for multi-tenancy in reactive we cannot use multiple datasource, one per tenant ?
Thanks
"Hibernate Reactive can only use the default datasource"
Yes, and that's documented here: https://quarkus.io/guides/hibernate-reactive#hr-limitations
does it mean that for multi-tenancy in reactive we cannot use multiple datasource, one per tenant ?
It seems the Hibernate Reactive extension for Quarkus doesn't support multi-tenancy at the moment: #15959
"static" configuration with named datasources certainly won't work due to this limitation.
A more dynamic approach where you create your own connections could work in theory, but I'm pretty sure it won't in practice because of the missing configuration mentioned in #15959.
Schema-based multitenancy is more likely to work to with Hibernate Reactive, FWIW. Though I'm not entirely sure either, since this I couldn't find relevant tests.
Thanks for the quick reply, i will continue the discussion on : https://github.com/quarkusio/quarkus/discussions/33342
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.
Hello all,
I ran into the same Issue not being able to use flyway migration in combination with a reactive data source. The workaround above works fine for me (running flyway manually on application startup).
Just FYI: I constantly got exceptions when I open the Quarkus DEV UI:
(executor-thread-1) Error in JsonRPC Call: java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
at io.smallrye.mutiny.unchecked.UncheckedSupplier.lambda$toSupplier$0(UncheckedSupplier.java:45)
at io.smallrye.context.impl.wrappers.SlowContextualSupplier.get(SlowContextualSupplier.java:21)
at io.smallrye.mutiny.operators.uni.builders.UniCreateFromItemSupplier.subscribe(UniCreateFromItemSupplier.java:28)
at io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
at io.smallrye.mutiny.operators.uni.UniRunSubscribeOn.lambda$subscribe$0(UniRunSubscribeOn.java:27)
at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:582)
at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2513)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1538)
at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.lang.reflect.InvocationTargetException
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.devui.runtime.comms.JsonRpcRouter.lambda$invoke$0(JsonRpcRouter.java:98)
at io.smallrye.mutiny.unchecked.UncheckedSupplier.lambda$toSupplier$0(UncheckedSupplier.java:41)
... 11 more
Caused by: java.lang.NullPointerException: Cannot invoke "io.quarkus.agroal.runtime.DataSources.getActiveDataSourceNames()" because the return value of "io.quarkus.arc.InstanceHandle.get()" is null
at io.quarkus.flyway.runtime.FlywayContainerUtil.getActiveFlywayContainers(FlywayContainerUtil.java:26)
at io.quarkus.flyway.runtime.FlywayContainersSupplier.get(FlywayContainersSupplier.java:16)
at io.quarkus.flyway.runtime.devui.FlywayJsonRpcService.getNumberOfDatasources(FlywayJsonRpcService.java:165)
at io.quarkus.flyway.runtime.devui.FlywayJsonRpcService_ClientProxy.getNumberOfDatasources(Unknown Source)
... 17 more
I fixed that by removing the quarkus-flyway
extension from the project. That also means there is no flyway tile in the Dev UI, but that's fine for me.
Cheers
I ran into the same Issue not being able to use flyway migration in combination with a reactive data source.
This was supposed to be fixed in https://github.com/quarkusio/quarkus/pull/36012, in Quarkus 3.5.0.CR1.
Can you please open a new issue with a reproducer?
Just FYI: I constantly got exceptions when I open the Quarkus DEV UI:
This looks like a bug too. Let's see what your reproducer looks like though, there must be something else at play.
Hi guys I'm using the following config in my environment (and it works ;D) I just tried to use a different datasource configuration for the migrations
`quarkus.datasource.db-kind = postgresql quarkus.datasource.username = postgres quarkus.datasource.password = postgres quarkus.datasource.reactive.url = vertx-reactive:postgresql://localhost:5432/mydb quarkus.datasource.jdbc = false
quarkus.flyway."flyway-migration".active = true quarkus.flyway."flyway-migration".migrate-at-start = true quarkus.datasource."flyway-migration".db-kind = postgresql quarkus.datasource."flyway-migration".jdbc.url = jdbc:postgresql://localhost:5432/mydb quarkus.datasource."flyway-migration".username = postgres quarkus.datasource."flyway-migration".password = postgres`
Hey, FWIW you don't need two datasources, so the right configuration would be something like this:
quarkus.datasource.db-kind = postgresql
quarkus.datasource.username = postgres
quarkus.datasource.password = postgres
quarkus.datasource.reactive.url = vertx-reactive:postgresql://localhost:5432/mydb
quarkus.datasource.jdbc.url = jdbc:postgresql://localhost:5432/mydb
quarkus.datasource.jdbc = false
quarkus.flyway.migrate-at-start = true
Or this if you use dev services:
quarkus.datasource.db-kind = postgresql
quarkus.datasource.username = postgres
quarkus.datasource.password = postgres
%prod.quarkus.datasource.reactive.url = vertx-reactive:postgresql://localhost:5432/mydb
%prod.quarkus.datasource.jdbc.url = jdbc:postgresql://localhost:5432/mydb
quarkus.datasource.jdbc = false
quarkus.flyway.migrate-at-start = true
I updated the documentation to make this clearer: https://quarkus.io/version/main/guides/flyway#reactive-datasources
Hi @yrodiere - I'm a recent victim of this scenario as well. I need Flyway working for local development and for other environments as well. Setting
quarkus.datasource.jdbc = false
Prevented Flyway from working locally for me. Is that expected? My final configuration looks like
quarkus.hibernate-orm.enabled=true
quarkus.hibernate-orm.database.generation=none
quarkus.hibernate-orm.log.sql=true
quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=<username>
quarkus.flyway.migrate-at-start=true
%stage.quarkus.datasource.credentials-provider=my-ssm-credentials-provider
%stage.quarkus.datasource.db-version=13.12
%stage.quarkus.datasource.reactive.url=vertx-reactive:postgresql://<host>
%stage.quarkus.datasource.jdbc.url=jdbc:postgresql://<host>
But now I fear that Hibernate Reactive does not use the correct reactive driver anymore (since JDBC is not disabled). Does this look correct to you? Thanks!
Hello
Setting
quarkus.datasource.jdbc = false
Prevented Flyway from working locally for me. Is that expected?
Yes. Flyway uses JDBC. You disable JDBC, Flyway doesn't work.
My final configuration looks like
quarkus.hibernate-orm.enabled=true quarkus.hibernate-orm.database.generation=none quarkus.hibernate-orm.log.sql=true quarkus.datasource.db-kind=postgresql quarkus.datasource.username=<username> quarkus.flyway.migrate-at-start=true %stage.quarkus.datasource.credentials-provider=my-ssm-credentials-provider %stage.quarkus.datasource.db-version=13.12 %stage.quarkus.datasource.reactive.url=vertx-reactive:postgresql://<host> %stage.quarkus.datasource.jdbc.url=jdbc:postgresql://<host>
But now I fear that Hibernate Reactive does not use the correct reactive driver anymore (since JDBC is not disabled).
Then fear not: Hibernate Reactive is absolutely incapable of using JDBC.
Does this look correct to you?
Yes.
Describe the bug Simply adding the
quarkus-flyway
extension when using Hibernate Reactive causes: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:
Take hibernate-reactive-quickstart
Add the following dependencies:
mvn quarkus:dev
Environment (please complete the following information):
java -version
: openjdk version "11.0.7" 2020-04-14Additional context (Add any other context about the problem here.)