Skip to content

Commit a4769b3

Browse files
committed
Merge pull request #25708 from bono007
* gh-25708: Polish "Provide health for an AbstractRoutingDataSource's resolved targets" Provide health for an AbstractRoutingDataSource's resolved targets Closes gh-25708
2 parents 710a905 + 0a8da4f commit a4769b3

File tree

3 files changed

+67
-19
lines changed

3 files changed

+67
-19
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthContributorAutoConfiguration.java

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2020 the original author or authors.
2+
* Copyright 2012-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,19 +17,19 @@
1717
package org.springframework.boot.actuate.autoconfigure.jdbc;
1818

1919
import java.util.Collection;
20+
import java.util.Iterator;
2021
import java.util.Map;
22+
import java.util.function.Function;
2123
import java.util.stream.Collectors;
2224

2325
import javax.sql.DataSource;
2426

2527
import org.springframework.beans.factory.InitializingBean;
2628
import org.springframework.beans.factory.ObjectProvider;
27-
import org.springframework.boot.actuate.autoconfigure.health.CompositeHealthContributorConfiguration;
2829
import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator;
29-
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
30-
import org.springframework.boot.actuate.health.Health.Builder;
30+
import org.springframework.boot.actuate.health.CompositeHealthContributor;
3131
import org.springframework.boot.actuate.health.HealthContributor;
32-
import org.springframework.boot.actuate.health.HealthIndicator;
32+
import org.springframework.boot.actuate.health.NamedContributor;
3333
import org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator;
3434
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
3535
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@@ -45,6 +45,7 @@
4545
import org.springframework.context.annotation.Configuration;
4646
import org.springframework.jdbc.core.JdbcTemplate;
4747
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
48+
import org.springframework.util.Assert;
4849

4950
/**
5051
* {@link EnableAutoConfiguration Auto-configuration} for
@@ -64,8 +65,7 @@
6465
@ConditionalOnEnabledHealthIndicator("db")
6566
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
6667
@EnableConfigurationProperties(DataSourceHealthIndicatorProperties.class)
67-
public class DataSourceHealthContributorAutoConfiguration extends
68-
CompositeHealthContributorConfiguration<AbstractHealthIndicator, DataSource> implements InitializingBean {
68+
public class DataSourceHealthContributorAutoConfiguration implements InitializingBean {
6969

7070
private final Collection<DataSourcePoolMetadataProvider> metadataProviders;
7171

@@ -94,10 +94,18 @@ public HealthContributor dbHealthContributor(Map<String, DataSource> dataSources
9494
return createContributor(dataSources);
9595
}
9696

97-
@Override
98-
protected AbstractHealthIndicator createIndicator(DataSource source) {
97+
private HealthContributor createContributor(Map<String, DataSource> beans) {
98+
Assert.notEmpty(beans, "Beans must not be empty");
99+
if (beans.size() == 1) {
100+
return createIndicator(beans.values().iterator().next());
101+
}
102+
return CompositeHealthContributor.fromMap(beans, this::createIndicator);
103+
}
104+
105+
private HealthContributor createIndicator(DataSource source) {
99106
if (source instanceof AbstractRoutingDataSource) {
100-
return new RoutingDataSourceHealthIndicator();
107+
AbstractRoutingDataSource routingDataSource = (AbstractRoutingDataSource) source;
108+
return new RoutingDataSourceHealthIndicator(routingDataSource, this::createIndicator);
101109
}
102110
return new DataSourceHealthIndicator(source, getValidationQuery(source));
103111
}
@@ -108,14 +116,29 @@ private String getValidationQuery(DataSource source) {
108116
}
109117

110118
/**
111-
* {@link HealthIndicator} used for {@link AbstractRoutingDataSource} beans where we
112-
* can't actually query for the status.
119+
* {@link CompositeHealthContributor} used for {@link AbstractRoutingDataSource} beans
120+
* where the overall health is composed of a {@link DataSourceHealthIndicator} for
121+
* each routed datasource.
113122
*/
114-
static class RoutingDataSourceHealthIndicator extends AbstractHealthIndicator {
123+
static class RoutingDataSourceHealthIndicator implements CompositeHealthContributor {
124+
125+
private CompositeHealthContributor delegate;
126+
127+
RoutingDataSourceHealthIndicator(AbstractRoutingDataSource routingDataSource,
128+
Function<DataSource, HealthContributor> indicatorFunction) {
129+
Map<String, DataSource> routedDataSources = routingDataSource.getResolvedDataSources().entrySet().stream()
130+
.collect(Collectors.toMap((e) -> e.getKey().toString(), Map.Entry::getValue));
131+
this.delegate = CompositeHealthContributor.fromMap(routedDataSources, indicatorFunction);
132+
}
133+
134+
@Override
135+
public HealthContributor getContributor(String name) {
136+
return this.delegate.getContributor(name);
137+
}
115138

116139
@Override
117-
protected void doHealthCheck(Builder builder) throws Exception {
118-
builder.unknown().withDetail("routing", true);
140+
public Iterator<NamedContributor<HealthContributor>> iterator() {
141+
return this.delegate.iterator();
119142
}
120143

121144
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthContributorAutoConfigurationTests.java

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616

1717
package org.springframework.boot.actuate.autoconfigure.jdbc;
1818

19+
import java.util.HashMap;
20+
import java.util.Map;
21+
1922
import javax.sql.DataSource;
2023

2124
import org.junit.jupiter.api.Test;
@@ -38,6 +41,7 @@
3841
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
3942

4043
import static org.assertj.core.api.Assertions.assertThat;
44+
import static org.mockito.BDDMockito.given;
4145
import static org.mockito.Mockito.mock;
4246

4347
/**
@@ -93,9 +97,16 @@ void runWithRoutingAndEmbeddedDataSourceShouldNotIncludeRoutingDataSourceWhenIgn
9397
}
9498

9599
@Test
96-
void runWithOnlyRoutingDataSourceShouldIncludeRoutingDataSource() {
97-
this.contextRunner.withUserConfiguration(RoutingDataSourceConfig.class)
98-
.run((context) -> assertThat(context).hasSingleBean(RoutingDataSourceHealthIndicator.class));
100+
void runWithOnlyRoutingDataSourceShouldIncludeRoutingDataSourceWithComposedIndicators() {
101+
this.contextRunner.withUserConfiguration(RoutingDataSourceConfig.class).run((context) -> {
102+
assertThat(context).hasSingleBean(RoutingDataSourceHealthIndicator.class);
103+
RoutingDataSourceHealthIndicator routingHealthContributor = context
104+
.getBean(RoutingDataSourceHealthIndicator.class);
105+
assertThat(routingHealthContributor.getContributor("one")).isInstanceOf(DataSourceHealthIndicator.class);
106+
assertThat(routingHealthContributor.getContributor("two")).isInstanceOf(DataSourceHealthIndicator.class);
107+
assertThat(routingHealthContributor.iterator()).toIterable().extracting("name")
108+
.containsExactlyInAnyOrder("one", "two");
109+
});
99110
}
100111

101112
@Test
@@ -143,7 +154,12 @@ static class RoutingDataSourceConfig {
143154

144155
@Bean
145156
AbstractRoutingDataSource routingDataSource() {
146-
return mock(AbstractRoutingDataSource.class);
157+
Map<Object, DataSource> dataSources = new HashMap<>();
158+
dataSources.put("one", mock(DataSource.class));
159+
dataSources.put("two", mock(DataSource.class));
160+
AbstractRoutingDataSource routingDataSource = mock(AbstractRoutingDataSource.class);
161+
given(routingDataSource.getResolvedDataSources()).willReturn(dataSources);
162+
return routingDataSource;
147163
}
148164

149165
}

spring-boot-project/spring-boot-docs/src/docs/asciidoc/production-ready-features.adoc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -989,6 +989,15 @@ TIP: You can use `@Qualifier("groupname")` if you need to register custom `Statu
989989

990990

991991

992+
[[production-ready-health-datasource]]
993+
==== DataSource Health
994+
The `DataSource` health indicator shows the health of both standard data source and routing data source beans.
995+
The health of a routing data source includes the health of each of its target data sources.
996+
In the health endpoint's response, each of a routing data source's targets is named using its routing key.
997+
If you prefer not to include routing data sources in the indicator's output, set configprop:management.health.db.ignore-routing-data-sources[] to `true`.
998+
999+
1000+
9921001
[[production-ready-kubernetes-probes]]
9931002
=== Kubernetes Probes
9941003
Applications deployed on Kubernetes can provide information about their internal state with https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes[Container Probes].

0 commit comments

Comments
 (0)