Skip to content

Commit

Permalink
Merge pull request quarkusio#38108 from yrodiere/datasource-active
Browse files Browse the repository at this point in the history
Add runtime configuration property `quarkus.datasource.active`
  • Loading branch information
yrodiere authored Jan 17, 2024
2 parents 621c22c + 4f63e89 commit e7c8069
Show file tree
Hide file tree
Showing 78 changed files with 3,075 additions and 278 deletions.
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
93 changes: 93 additions & 0 deletions docs/src/main/asciidoc/datasource.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,99 @@ AgroalDataSource usersDataSource;
AgroalDataSource inventoryDataSource;
----

[[datasource-active]]
=== Activate/deactivate datasources

If a datasource is configured at build time,
by default it is active at runtime,
that is 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.
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.

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

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.

[TIP]
====
xref:config-reference.adoc#custom-profiles[Custom configuration profiles] can help simplify such a setup.
By appending the following profile-specific configuration to the one above,
you can select a persistence unit/datasource at runtime simply by
xref:config-reference.adoc#multiple-profiles[setting `quarkus.profile`]:
`quarkus.profile=prod,pg` or `quarkus.profile=prod,oracle`.
[source,properties]
----
%pg.quarkus.hibernate-orm."pg".active=true
%pg.quarkus.datasource."pg".active=true
# Add any pg-related runtime configuration here, prefixed with "%pg."
%oracle.quarkus.hibernate-orm."oracle".active=true
%oracle.quarkus.datasource."oracle".active=true
# Add any pg-related runtime configuration here, prefixed with "%pg."
----
====

[TIP]
====
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
92 changes: 92 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,98 @@ You can inject the `EntityManagerFactory` of a named persistence unit using the
EntityManagerFactory entityManagerFactory;
----

[[persistence-unit-active]]
=== Activate/deactivate persistence units

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

To deactivate a persistence unit at runtime, set `quarkus.hibernate-orm[.optional name].active` to `false`.
Then Quarkus will not start the corresponding Hibernate ORM `SessionFactory` on application startup.
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
----

xref:config-reference.adoc#configuration-sources[Setting] `quarkus.hibernate-orm."pg".active=true` and `quarkus.datasource."pg".active=true` 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.

[TIP]
====
xref:config-reference.adoc#custom-profiles[Custom configuration profiles] can help simplify such a setup.
By appending the following profile-specific configuration to the one above,
you can select a persistence unit/datasource at runtime simply by
xref:config-reference.adoc#multiple-profiles[setting `quarkus.profile`]:
`quarkus.profile=prod,pg` or `quarkus.profile=prod,oracle`.
[source,properties]
----
%pg.quarkus.hibernate-orm."pg".active=true
%pg.quarkus.datasource."pg".active=true
# Add any pg-related runtime configuration here, prefixed with "%pg."
%oracle.quarkus.hibernate-orm."oracle".active=true
%oracle.quarkus.datasource."oracle".active=true
# Add any pg-related runtime configuration here, prefixed with "%pg."
----
====

[TIP]
====
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
Expand Up @@ -24,10 +24,10 @@
import io.agroal.api.AgroalDataSource;
import io.agroal.api.AgroalPoolInterceptor;
import io.quarkus.agroal.DataSource;
import io.quarkus.agroal.runtime.AgroalDataSourceSupport;
import io.quarkus.agroal.runtime.AgroalDataSourcesInitializer;
import io.quarkus.agroal.runtime.AgroalRecorder;
import io.quarkus.agroal.runtime.DataSourceJdbcBuildTimeConfig;
import io.quarkus.agroal.runtime.DataSourceSupport;
import io.quarkus.agroal.runtime.DataSources;
import io.quarkus.agroal.runtime.DataSourcesJdbcBuildTimeConfig;
import io.quarkus.agroal.runtime.JdbcDriver;
Expand Down Expand Up @@ -202,20 +202,20 @@ private static void validateBuildTimeConfig(AggregatedDataSourceBuildTimeConfigB
}
}

private DataSourceSupport getDataSourceSupport(
private AgroalDataSourceSupport getDataSourceSupport(
List<AggregatedDataSourceBuildTimeConfigBuildItem> aggregatedBuildTimeConfigBuildItems,
SslNativeConfigBuildItem sslNativeConfig, Capabilities capabilities) {
Map<String, DataSourceSupport.Entry> dataSourceSupportEntries = new HashMap<>();
Map<String, AgroalDataSourceSupport.Entry> dataSourceSupportEntries = new HashMap<>();
for (AggregatedDataSourceBuildTimeConfigBuildItem aggregatedDataSourceBuildTimeConfig : aggregatedBuildTimeConfigBuildItems) {
String dataSourceName = aggregatedDataSourceBuildTimeConfig.getName();
dataSourceSupportEntries.put(dataSourceName,
new DataSourceSupport.Entry(dataSourceName, aggregatedDataSourceBuildTimeConfig.getDbKind(),
new AgroalDataSourceSupport.Entry(dataSourceName, aggregatedDataSourceBuildTimeConfig.getDbKind(),
aggregatedDataSourceBuildTimeConfig.getDataSourceConfig().dbVersion(),
aggregatedDataSourceBuildTimeConfig.getResolvedDriverClass(),
aggregatedDataSourceBuildTimeConfig.isDefault()));
}

return new DataSourceSupport(sslNativeConfig.isExplicitlyDisabled(),
return new AgroalDataSourceSupport(sslNativeConfig.isExplicitlyDisabled(),
capabilities.isPresent(Capability.METRICS), dataSourceSupportEntries);
}

Expand Down Expand Up @@ -247,10 +247,11 @@ void generateDataSourceSupportBean(AgroalRecorder recorder,
unremovableBeans.produce(UnremovableBeanBuildItem.beanTypes(AgroalPoolInterceptor.class));

// create the DataSourceSupport bean that DataSourceProducer uses as a dependency
DataSourceSupport dataSourceSupport = getDataSourceSupport(aggregatedBuildTimeConfigBuildItems, sslNativeConfig,
AgroalDataSourceSupport agroalDataSourceSupport = getDataSourceSupport(aggregatedBuildTimeConfigBuildItems,
sslNativeConfig,
capabilities);
syntheticBeanBuildItemBuildProducer.produce(SyntheticBeanBuildItem.configure(DataSourceSupport.class)
.supplier(recorder.dataSourceSupportSupplier(dataSourceSupport))
syntheticBeanBuildItemBuildProducer.produce(SyntheticBeanBuildItem.configure(AgroalDataSourceSupport.class)
.supplier(recorder.dataSourceSupportSupplier(agroalDataSourceSupport))
.unremovable()
.done());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package io.quarkus.agroal.test;

import jakarta.inject.Inject;

import org.eclipse.microprofile.metrics.Counter;
import org.eclipse.microprofile.metrics.Gauge;
import org.eclipse.microprofile.metrics.MetricID;
import org.eclipse.microprofile.metrics.MetricRegistry;
import org.eclipse.microprofile.metrics.Tag;
import org.eclipse.microprofile.metrics.annotation.RegistryType;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;

public class AgroalMetricsConfigActiveFalseTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withConfigurationResource("application-metrics-enabled.properties")
.overrideConfigKey("quarkus.datasource.active", "false")
.overrideConfigKey("quarkus.datasource.ds1.active", "false");

@Inject
@RegistryType(type = MetricRegistry.Type.VENDOR)
MetricRegistry registry;

@Test
public void testMetricsOfDefaultDS() {
Counter acquireCount = registry.getCounters()
.get(new MetricID("agroal.acquire.count", new Tag("datasource", "default")));
Gauge<?> maxUsed = registry.getGauges()
.get(new MetricID("agroal.max.used.count", new Tag("datasource", "default")));

Assertions.assertNull(acquireCount, "Agroal metrics should not be registered for deactivated datasources eagerly");
Assertions.assertNull(maxUsed, "Agroal metrics should not be registered for deactivated datasources eagerly");
}

@Test
public void testMetricsOfDs1() {
Counter acquireCount = registry.getCounters().get(new MetricID("agroal.acquire.count",
new Tag("datasource", "ds1")));
Gauge<?> maxUsed = registry.getGauges().get(new MetricID("agroal.max.used.count",
new Tag("datasource", "ds1")));

Assertions.assertNull(acquireCount, "Agroal metrics should not be registered for deactivated datasources eagerly");
Assertions.assertNull(maxUsed, "Agroal metrics should not be registered for deactivated datasources eagerly");
}

}
Loading

0 comments on commit e7c8069

Please sign in to comment.