Skip to content

Commit

Permalink
fixup! Add runtime configuration property quarkus.datasource.active
Browse files Browse the repository at this point in the history
  • Loading branch information
yrodiere committed Jan 9, 2024
1 parent 9705cae commit ba080c7
Show file tree
Hide file tree
Showing 10 changed files with 593 additions and 1 deletion.
2 changes: 1 addition & 1 deletion docs/src/main/asciidoc/cdi.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ Client proxies allow for:
* Circular dependencies in the dependency graph. Having circular dependencies is often an indication that a redesign should be considered, but sometimes it's inevitable.
* In rare cases it's practical to destroy the beans manually. A direct injected reference would lead to a stale bean instance.
[[ok-you-said-that-there-are-several-kinds-of-beans]]
== OK. You said that there are several kinds of beans?
Yes. In general, we distinguish:
Expand Down
1 change: 1 addition & 0 deletions docs/src/main/asciidoc/config-reference.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ By default, Quarkus provides three profiles, that activate automatically in cert
* *test* - Activated when running tests
* *prod* - The default profile when not running in development or test mode

[[custom-profiles]]
=== Custom Profiles

It is also possible to create additional profiles and activate them with the `quarkus.profile` configuration property. A
Expand Down
77 changes: 77 additions & 0 deletions docs/src/main/asciidoc/datasource.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,83 @@ AgroalDataSource usersDataSource;
AgroalDataSource inventoryDataSource;
----

[[datasource-active]]
=== Activating/deactivating datasources

If a datasource is configured at build time,
by default it is active at runtime,
i.e. Quarkus will start the corresponding JDBC connection pool or reactive client on application startup.

To deactivate a datasource at runtime, set `quarkus.datasource[.optional name].active` to `false`.
Then Quarkus will not start the corresponding JDBC connection pool or reactive client on application startup,
and any attempt to use the corresponding datasource at runtime will fail with a clear error message.

This is in particular useful when you want an application to be able
to use one of a pre-determined set of datasources at runtime.

For example, with the following configuration:

[source,properties]
----
quarkus.datasource."pg".db-kind=postgres
quarkus.datasource."pg".active=false
quarkus.datasource."pg".jdbc.url=jdbc:postgresql:///your_database
quarkus.datasource."oracle".db-kind=oracle
quarkus.datasource."oracle".active=false
quarkus.datasource."oracle".jdbc.url=jdbc:oracle:///your_database
----

Setting `quarkus.datasource."pg".active=true` xref:config-reference.adoc#configuration-sources[at runtime]
will make only the PostgreSQL datasource available,
and setting `quarkus.datasource."oracle".active=true` at runtime
will make only the Oracle datasource available.

[WARNING]
====
If another Quarkus extension relies on an inactive datasource,
that extension may fail to start.
In such case, you will need configure that extension to deactivate it too.
For example see xref:hibernate-orm.adoc#persistence-unit-active[here for Hibernate ORM].
====

[NOTE]
====
xref:config-reference.adoc#custom-profiles[Custom configuration profiles] can help simplify such a setup,
by regrouping configuration for a given datasource in a given profile,
allowing to switch simply by selecting a different profile.
It can also be useful to define a xref:cdi.adoc#ok-you-said-that-there-are-several-kinds-of-beans[CDI bean producer] redirecting to the currently active datasource,
like this:
[source,java,indent=0]
----
public class MyProducer {
@Inject
DataSourceSupport dataSourceSupport;
@Inject
@DataSource("pg")
AgroalDataSource pgDataSourceBean;
@Inject
@DataSource("oracle")
AgroalDataSource oracleDataSourceBean;
@Produces
@ApplicationScoped
public AgroalDataSource dataSource() {
if (dataSourceSupport.getInactiveNames().contains("pg")) {
return oracleDataSourceBean;
} else {
return pgDataSourceBean;
}
}
}
----
====

== Datasource integrations

=== Datasource health check
Expand Down
76 changes: 76 additions & 0 deletions docs/src/main/asciidoc/hibernate-orm.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,82 @@ You can inject the `EntityManagerFactory` of a named persistence unit using the
EntityManagerFactory entityManagerFactory;
----

[[persistence-unit-active]]
=== Activating/deactivating persistence units

If a persistence unit is configured at build time,
by default it is active at runtime,
i.e. Quarkus will start the corresponding Hibernate ORM `SessionFactory` on application startup.

To deactivate a persistence unit at runtime, set `quarkus.datasource[.optional name].active` to `false`.
Then Quarkus will not start the corresponding Hibernate ORM `SessionFactory` on application startup,
and any attempt to use the corresponding persistence unit at runtime will fail with a clear error message.

This is in particular useful when you want an application to be able
to xref:datasource.adoc#datasource-active[use one of a pre-determined set of datasources at runtime].

For example, with the following configuration:

[source,properties]
----
quarkus.hibernate-orm."pg".packages=org.acme.model.shared
quarkus.hibernate-orm."pg".datasource=pg
quarkus.hibernate-orm."pg".database.generation=drop-and-create
quarkus.hibernate-orm."pg".active=false
quarkus.datasource."pg".db-kind=h2
quarkus.datasource."pg".active=false
quarkus.datasource."pg".jdbc.url=jdbc:postgresql:///your_database
quarkus.hibernate-orm."oracle".packages=org.acme.model.shared
quarkus.hibernate-orm."oracle".datasource=oracle
quarkus.hibernate-orm."oracle".database.generation=drop-and-create
quarkus.hibernate-orm."oracle".active=false
quarkus.datasource."oracle".db-kind=oracle
quarkus.datasource."oracle".active=false
quarkus.datasource."oracle".jdbc.url=jdbc:oracle:///your_database
----

Setting `quarkus.hibernate-orm."pg".active=true` and `quarkus.datasource."pg".active=true` xref:config-reference.adoc#configuration-sources[at runtime]
will make only the PostgreSQL persistence unit and datasource available,
and setting `quarkus.hibernate-orm."oracle".active=true` and `quarkus.datasource."oracle".active=true` at runtime
will make only the Oracle persistence unit and datasource available.

[NOTE]
====
xref:config-reference.adoc#custom-profiles[Custom configuration profiles] can help simplify such a setup,
by regrouping configuration for a given datasource in a given profile,
allowing to switch simply by selecting a different profile.
It can also be useful to define a xref:cdi.adoc#ok-you-said-that-there-are-several-kinds-of-beans[CDI bean producer] redirecting to the currently active persistence unit,
like this:
[source,java,indent=0]
----
public class MyProducer {
@Inject
DataSourceSupport dataSourceSupport;
@Inject
@PersistenceUnit("pg")
Session pgSessionBean;
@Inject
@PersistenceUnit("oracle")
Session oracleSessionBean;
@Produces
@ApplicationScoped
public Session session() {
if (dataSourceSupport.getInactiveNames().contains("pg")) {
return oracleSessionBean;
} else {
return pgSessionBean;
}
}
}
----
====

[[persistence-xml]]
== Setting up and configuring Hibernate ORM with a `persistence.xml`

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package io.quarkus.agroal.test;

import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Produces;
import jakarta.inject.Inject;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.agroal.api.AgroalDataSource;
import io.quarkus.agroal.DataSource;
import io.quarkus.datasource.runtime.DataSourceSupport;
import io.quarkus.test.QuarkusUnitTest;

/**
* Tests a use case where multiple datasources are defined at build time,
* but only one is used at runtime.
* <p>
* This is mostly useful when each datasource has a distinct db-kind, but in theory that shouldn't matter,
* so we use the h2 db-kind everywhere here to keep test dependencies simpler.
* <p>
* See {@link MultipleDataSourcesAsAlternativesWithActiveDS1Test} for the counterpart where PU2 is used at runtime.
*/
public class MultipleDataSourcesAsAlternativesWithActiveDS1Test {

@RegisterExtension
static QuarkusUnitTest runner = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addClass(MyProducer.class))
.overrideConfigKey("quarkus.datasource.ds-1.db-kind", "h2")
.overrideConfigKey("quarkus.datasource.ds-1.active", "false")
.overrideConfigKey("quarkus.datasource.ds-2.db-kind", "h2")
.overrideConfigKey("quarkus.datasource.ds-2.active", "false")
// This is where we select datasource 1
.overrideRuntimeConfigKey("quarkus.datasource.ds-1.active", "true")
.overrideRuntimeConfigKey("quarkus.datasource.ds-1.jdbc.url", "jdbc:h2:mem:testds1");

@Inject
@DataSource("ds-1")
AgroalDataSource explicitDatasourceBean;

@Inject
AgroalDataSource customIndirectDatasourceBean;

@Inject
@DataSource("ds-2")
AgroalDataSource inactiveDatasourceBean;

@Test
public void testExplicitDatasourceBeanUsable() {
doTestDatasource(explicitDatasourceBean);
}

@Test
public void testCustomIndirectDatasourceBeanUsable() {
doTestDatasource(customIndirectDatasourceBean);
}

@Test
public void testInactiveDatasourceBeanUnusable() {
assertThatThrownBy(() -> inactiveDatasourceBean.getConnection())
.hasMessageContaining("Datasource 'ds-2' was deactivated through configuration properties.");
}

private static void doTestDatasource(AgroalDataSource dataSource) {
assertThatCode(() -> {
try (var connection = dataSource.getConnection()) {
}
})
.doesNotThrowAnyException();
}

private static class MyProducer {
@Inject
DataSourceSupport dataSourceSupport;

@Inject
@DataSource("ds-1")
AgroalDataSource dataSource1Bean;

@Inject
@DataSource("ds-2")
AgroalDataSource dataSource2Bean;

@Produces
@ApplicationScoped
public AgroalDataSource dataSource() {
if (dataSourceSupport.getInactiveNames().contains("ds-1")) {
return dataSource2Bean;
} else {
return dataSource1Bean;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package io.quarkus.agroal.test;

import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Produces;
import jakarta.inject.Inject;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.agroal.api.AgroalDataSource;
import io.quarkus.agroal.DataSource;
import io.quarkus.datasource.runtime.DataSourceSupport;
import io.quarkus.test.QuarkusUnitTest;

/**
* Tests a use case where multiple datasources are defined at build time,
* but only one is used at runtime.
* <p>
* This is mostly useful when each datasource has a distinct db-kind, but in theory that shouldn't matter,
* so we use the h2 db-kind everywhere here to keep test dependencies simpler.
* <p>
* See {@link MultipleDataSourcesAsAlternativesWithActiveDS1Test} for the counterpart where PU2 is used at runtime.
*/
public class MultipleDataSourcesAsAlternativesWithActiveDS2Test {

@RegisterExtension
static QuarkusUnitTest runner = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addClass(MyProducer.class))
.overrideConfigKey("quarkus.datasource.ds-1.db-kind", "h2")
.overrideConfigKey("quarkus.datasource.ds-1.active", "false")
.overrideConfigKey("quarkus.datasource.ds-2.db-kind", "h2")
.overrideConfigKey("quarkus.datasource.ds-2.active", "false")
// This is where we select datasource 2
.overrideRuntimeConfigKey("quarkus.datasource.ds-2.active", "true")
.overrideRuntimeConfigKey("quarkus.datasource.ds-2.jdbc.url", "jdbc:h2:mem:testds2");

@Inject
@DataSource("ds-2")
AgroalDataSource explicitDatasourceBean;

@Inject
AgroalDataSource customIndirectDatasourceBean;

@Inject
@DataSource("ds-1")
AgroalDataSource inactiveDatasourceBean;

@Test
public void testExplicitDatasourceBeanUsable() {
doTestDatasource(explicitDatasourceBean);
}

@Test
public void testCustomIndirectDatasourceBeanUsable() {
doTestDatasource(customIndirectDatasourceBean);
}

@Test
public void testInactiveDatasourceBeanUnusable() {
assertThatThrownBy(() -> inactiveDatasourceBean.getConnection())
.hasMessageContaining("Datasource 'ds-1' was deactivated through configuration properties.");
}

private static void doTestDatasource(AgroalDataSource dataSource) {
assertThatCode(() -> {
try (var connection = dataSource.getConnection()) {
}
})
.doesNotThrowAnyException();
}

private static class MyProducer {
@Inject
DataSourceSupport dataSourceSupport;

@Inject
@DataSource("ds-1")
AgroalDataSource dataSource1Bean;

@Inject
@DataSource("ds-2")
AgroalDataSource dataSource2Bean;

@Produces
@ApplicationScoped
public AgroalDataSource dataSource() {
if (dataSourceSupport.getInactiveNames().contains("ds-1")) {
return dataSource2Bean;
} else {
return dataSource1Bean;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ public interface DataSourceRuntimeConfig {
/**
* Whether this datasource should be active at runtime.
*
* See xref:datasource.adoc#datasource-active[this section of the documentation].
*
* If the datasource is not active, it won't start with the application,
* and accessing the corresponding Datasource CDI bean will fail,
* meaning in particular that consumers of this datasource
Expand Down
Loading

0 comments on commit ba080c7

Please sign in to comment.