Commit c5138c56 authored by Phillip Webb's avatar Phillip Webb

Restore AbstractRoutingDataSource health support

Update `DataSourceHealthContributorAutoConfiguration` so that any
`AbstractRoutingDataSource` beans are still included in the overall
health. Prior to this commit, a regression in Spring Boot 2.2 meant
that if a single routing bean was found an `IllegalArgumentException`
would be thrown.

In Spring Boot 2.1 all `AbstractRoutingDataSource` would be filtered
from the results, but if no results existed the following was returned:

  "details": {
    "db": {
      "status": "UNKNOWN"
    },

In Spring Boot 2.2 we now always include routing datasource beans, even
if other non-routing database beans are found. The health details
includes `"routing" : true` to help users disambiguate any results.

Fixes gh-18661
parent ba30ee03
......@@ -17,7 +17,6 @@
package org.springframework.boot.actuate.autoconfigure.jdbc;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;
......@@ -27,7 +26,10 @@ import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.health.CompositeHealthContributorConfiguration;
import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator;
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.health.Health.Builder;
import org.springframework.boot.actuate.health.HealthContributor;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
......@@ -60,7 +62,7 @@ import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
@ConditionalOnEnabledHealthIndicator("db")
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class DataSourceHealthContributorAutoConfiguration extends
CompositeHealthContributorConfiguration<DataSourceHealthIndicator, DataSource> implements InitializingBean {
CompositeHealthContributorConfiguration<AbstractHealthIndicator, DataSource> implements InitializingBean {
private final Collection<DataSourcePoolMetadataProvider> metadataProviders;
......@@ -79,24 +81,14 @@ public class DataSourceHealthContributorAutoConfiguration extends
@Bean
@ConditionalOnMissingBean(name = { "dbHealthIndicator", "dbHealthContributor" })
public HealthContributor dbHealthContributor(Map<String, DataSource> dataSources) {
return createContributor(filterDataSources(dataSources));
}
private Map<String, DataSource> filterDataSources(Map<String, DataSource> candidates) {
if (candidates == null) {
return null;
}
Map<String, DataSource> dataSources = new LinkedHashMap<>();
candidates.forEach((name, dataSource) -> {
if (!(dataSource instanceof AbstractRoutingDataSource)) {
dataSources.put(name, dataSource);
}
});
return dataSources;
return createContributor(dataSources);
}
@Override
protected DataSourceHealthIndicator createIndicator(DataSource source) {
protected AbstractHealthIndicator createIndicator(DataSource source) {
if (source instanceof AbstractRoutingDataSource) {
return new RoutingDataSourceHealthIndicator();
}
return new DataSourceHealthIndicator(source, getValidationQuery(source));
}
......@@ -105,4 +97,17 @@ public class DataSourceHealthContributorAutoConfiguration extends
return (poolMetadata != null) ? poolMetadata.getValidationQuery() : null;
}
/**
* {@link HealthIndicator} used for {@link AbstractRoutingDataSource} beans where we
* can't actually query for the status.
*/
static class RoutingDataSourceHealthIndicator extends AbstractHealthIndicator {
@Override
protected void doHealthCheck(Builder builder) throws Exception {
builder.unknown().withDetail("routing", true);
}
}
}
......@@ -21,6 +21,7 @@ import javax.sql.DataSource;
import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.jdbc.DataSourceHealthContributorAutoConfiguration.RoutingDataSourceHealthIndicator;
import org.springframework.boot.actuate.health.CompositeHealthContributor;
import org.springframework.boot.actuate.health.NamedContributor;
import org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator;
......@@ -71,10 +72,20 @@ class DataSourceHealthContributorAutoConfigurationTests {
}
@Test
void runShouldFilterRoutingDataSource() {
void runWithRoutingAndEmbeddedDataSourceShouldFilterRoutingDataSource() {
this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, RoutingDatasourceConfig.class)
.run((context) -> assertThat(context).hasSingleBean(DataSourceHealthIndicator.class)
.doesNotHaveBean(CompositeHealthContributor.class));
.run((context) -> {
CompositeHealthContributor composite = context.getBean(CompositeHealthContributor.class);
assertThat(composite.getContributor("dataSource")).isInstanceOf(DataSourceHealthIndicator.class);
assertThat(composite.getContributor("routingDataSource"))
.isInstanceOf(RoutingDataSourceHealthIndicator.class);
});
}
@Test
void runWithOnlyRoutingDataSourceShouldFilterRoutingDataSource() {
this.contextRunner.withUserConfiguration(RoutingDatasourceConfig.class)
.run((context) -> assertThat(context).hasSingleBean(RoutingDataSourceHealthIndicator.class));
}
@Test
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment