Skip to content

Commit

Permalink
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 02471b8 commit 9705cae
Show file tree
Hide file tree
Showing 54 changed files with 1,163 additions and 246 deletions.
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");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package io.quarkus.agroal.test;

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

import java.sql.SQLException;

import javax.sql.DataSource;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.CreationException;
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.arc.Arc;
import io.quarkus.runtime.configuration.ConfigurationException;
import io.quarkus.test.QuarkusUnitTest;

public class ConfigActiveFalseDefaultDatasourceTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.overrideConfigKey("quarkus.datasource.active", "false");

@Inject
MyBean myBean;

@Test
public void dataSource() {
DataSource ds = Arc.container().instance(DataSource.class).get();

// The bean is always available to be injected during static init
// since we don't know whether the datasource will be active at runtime.
// So the bean cannot be null.
assertThat(ds).isNotNull();
// However, any attempt to use it at runtime will fail.
assertThatThrownBy(() -> ds.getConnection())
.isInstanceOf(RuntimeException.class)
.cause()
.isInstanceOf(ConfigurationException.class)
.hasMessageContainingAll("Datasource '<default>' was deactivated through configuration properties.",
"To solve this, avoid accessing this datasource at runtime, for instance by deactivating consumers (persistence units, ...).",
"Alternatively, activate the datasource by setting configuration property 'quarkus.datasource.active'"
+ " to 'true' and configure datasource '<default>'",
"Refer to https://quarkus.io/guides/datasource for guidance.");
}

@Test
public void agroalDataSource() {
AgroalDataSource ds = Arc.container().instance(AgroalDataSource.class).get();

// The bean is always available to be injected during static init
// since we don't know whether the datasource will be active at runtime.
// So the bean cannot be null.
assertThat(ds).isNotNull();
// However, any attempt to use it at runtime will fail.
assertThatThrownBy(() -> ds.getConnection())
.isInstanceOf(RuntimeException.class)
.cause()
.isInstanceOf(ConfigurationException.class)
.hasMessageContainingAll("Datasource '<default>' was deactivated through configuration properties.",
"To solve this, avoid accessing this datasource at runtime, for instance by deactivating consumers (persistence units, ...).",
"Alternatively, activate the datasource by setting configuration property 'quarkus.datasource.active'"
+ " to 'true' and configure datasource '<default>'",
"Refer to https://quarkus.io/guides/datasource for guidance.");
}

@Test
public void injectedBean() {
assertThatThrownBy(() -> myBean.useDatasource())
.isInstanceOf(CreationException.class)
.hasMessageContainingAll("Datasource '<default>' was deactivated through configuration properties.",
"To solve this, avoid accessing this datasource at runtime, for instance by deactivating consumers (persistence units, ...).",
"Alternatively, activate the datasource by setting configuration property 'quarkus.datasource.active'"
+ " to 'true' and configure datasource '<default>'",
"Refer to https://quarkus.io/guides/datasource for guidance.");
}

@ApplicationScoped
public static class MyBean {
@Inject
DataSource ds;

public void useDatasource() throws SQLException {
ds.getConnection();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package io.quarkus.agroal.test;

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

import java.sql.SQLException;

import javax.sql.DataSource;

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

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

import io.quarkus.arc.Arc;
import io.quarkus.runtime.configuration.ConfigurationException;
import io.quarkus.test.QuarkusUnitTest;

public class ConfigActiveFalseNamedDatasourceTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.overrideConfigKey("quarkus.datasource.users.active", "false")
// We need at least one build-time property for the datasource,
// otherwise it's considered unconfigured at build time...
.overrideConfigKey("quarkus.datasource.users.db-kind", "h2");

@Inject
MyBean myBean;

@Test
public void dataSource() {
DataSource ds = Arc.container().instance(DataSource.class,
new io.quarkus.agroal.DataSource.DataSourceLiteral("users")).get();

// The bean is always available to be injected during static init
// since we don't know whether the datasource will be active at runtime.
// So the bean cannot be null.
assertThat(ds).isNotNull();
// However, any attempt to use it at runtime will fail.
assertThatThrownBy(() -> ds.getConnection())
.isInstanceOf(RuntimeException.class)
.cause()
.isInstanceOf(ConfigurationException.class)
.hasMessageContainingAll("Datasource 'users' was deactivated through configuration properties.",
"To solve this, avoid accessing this datasource at runtime, for instance by deactivating consumers (persistence units, ...).",
"Alternatively, activate the datasource by setting configuration property 'quarkus.datasource.\"users\".active'"
+ " to 'true' and configure datasource 'users'",
"Refer to https://quarkus.io/guides/datasource for guidance.");
}

@Test
public void agroalDataSource() {
DataSource ds = Arc.container().instance(DataSource.class,
new io.quarkus.agroal.DataSource.DataSourceLiteral("users")).get();

// The bean is always available to be injected during static init
// since we don't know whether the datasource will be active at runtime.
// So the bean cannot be null.
assertThat(ds).isNotNull();
// However, any attempt to use it at runtime will fail.
assertThatThrownBy(() -> ds.getConnection())
.isInstanceOf(RuntimeException.class)
.cause()
.isInstanceOf(ConfigurationException.class)
.hasMessageContainingAll("Datasource 'users' was deactivated through configuration properties.",
"To solve this, avoid accessing this datasource at runtime, for instance by deactivating consumers (persistence units, ...).",
"Alternatively, activate the datasource by setting configuration property 'quarkus.datasource.\"users\".active'"
+ " to 'true' and configure datasource 'users'",
"Refer to https://quarkus.io/guides/datasource for guidance.");
}

@Test
public void injectedBean() {
assertThatThrownBy(() -> myBean.useDatasource())
.isInstanceOf(CreationException.class)
.hasMessageContainingAll("Datasource 'users' was deactivated through configuration properties.",
"To solve this, avoid accessing this datasource at runtime, for instance by deactivating consumers (persistence units, ...).",
"Alternatively, activate the datasource by setting configuration property 'quarkus.datasource.\"users\".active'"
+ " to 'true' and configure datasource 'users'",
"Refer to https://quarkus.io/guides/datasource for guidance.");
}

@ApplicationScoped
public static class MyBean {
@Inject
@io.quarkus.agroal.DataSource("users")
DataSource ds;

public void useDatasource() throws SQLException {
ds.getConnection();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package io.quarkus.agroal.test;

import org.hamcrest.CoreMatchers;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;
import io.restassured.RestAssured;

public class DataSourceHealthCheckConfigFalseTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot(jar -> jar.addAsResource(new StringAsset("""
quarkus.datasource.db-kind=h2
quarkus.datasource.username=username-default
quarkus.datasource.jdbc.url=jdbc:h2:tcp://localhost/mem:default
quarkus.datasource.jdbc.max-size=13
# this data source is broken, but will be deactivated,
# so the overall check should pass
quarkus.datasource.brokenDS.db-kind=h2
quarkus.datasource.brokenDS.jdbc.url=BROKEN
quarkus.datasource.health.enabled=true
quarkus.datasource.brokenDS.active=false
"""),
"application.properties"));

@Test
public void testDataSourceHealthCheckExclusion() {
RestAssured.when().get("/q/health/ready")
.then()
.body("status", CoreMatchers.equalTo("UP"));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
import java.util.Map;
import java.util.Optional;

public class DataSourceSupport {
public class AgroalDataSourceSupport {

public final boolean disableSslSupport;
public final boolean mpMetricsPresent;
public final Map<String, Entry> entries;

public DataSourceSupport(boolean disableSslSupport, boolean mpMetricsPresent, Map<String, Entry> entries) {
public AgroalDataSourceSupport(boolean disableSslSupport, boolean mpMetricsPresent, Map<String, Entry> entries) {
this.disableSslSupport = disableSslSupport;
this.mpMetricsPresent = mpMetricsPresent;
this.entries = entries;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
@Recorder
public class AgroalRecorder {

public Supplier<DataSourceSupport> dataSourceSupportSupplier(DataSourceSupport dataSourceSupport) {
return new Supplier<DataSourceSupport>() {
public Supplier<AgroalDataSourceSupport> dataSourceSupportSupplier(AgroalDataSourceSupport agroalDataSourceSupport) {
return new Supplier<AgroalDataSourceSupport>() {
@Override
public DataSourceSupport get() {
return dataSourceSupport;
public AgroalDataSourceSupport get() {
return agroalDataSourceSupport;
}
};
}
Expand Down
Loading

0 comments on commit 9705cae

Please sign in to comment.