Skip to content

Commit a023bd0

Browse files
committed
Add configurable timeout for Couchbase health indicator
This commit makes sure to use a configurable timeout to check if the Couchbase cluster is up, rather than relying on the default that can be quite long. Closes gh-13879
1 parent 21691f0 commit a023bd0

File tree

6 files changed

+141
-13
lines changed

6 files changed

+141
-13
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorAutoConfiguration.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2017 the original author or authors.
2+
* Copyright 2012-2018 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.
@@ -32,6 +32,7 @@
3232
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
3333
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
3434
import org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration;
35+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
3536
import org.springframework.context.annotation.Bean;
3637
import org.springframework.context.annotation.Configuration;
3738
import org.springframework.data.couchbase.core.CouchbaseOperations;
@@ -41,6 +42,7 @@
4142
* {@link CouchbaseHealthIndicator}.
4243
*
4344
* @author Eddú Meléndez
45+
* @author Stephane Nicoll
4446
* @since 2.0.0
4547
*/
4648
@Configuration
@@ -49,14 +51,19 @@
4951
@ConditionalOnEnabledHealthIndicator("couchbase")
5052
@AutoConfigureBefore(HealthIndicatorAutoConfiguration.class)
5153
@AutoConfigureAfter(CouchbaseDataAutoConfiguration.class)
54+
@EnableConfigurationProperties(CouchbaseHealthIndicatorProperties.class)
5255
public class CouchbaseHealthIndicatorAutoConfiguration extends
5356
CompositeHealthIndicatorConfiguration<CouchbaseHealthIndicator, CouchbaseOperations> {
5457

5558
private final Map<String, CouchbaseOperations> couchbaseOperations;
5659

60+
private final CouchbaseHealthIndicatorProperties properties;
61+
5762
public CouchbaseHealthIndicatorAutoConfiguration(
58-
Map<String, CouchbaseOperations> couchbaseOperations) {
63+
Map<String, CouchbaseOperations> couchbaseOperations,
64+
CouchbaseHealthIndicatorProperties properties) {
5965
this.couchbaseOperations = couchbaseOperations;
66+
this.properties = properties;
6067
}
6168

6269
@Bean
@@ -65,4 +72,11 @@ public HealthIndicator couchbaseHealthIndicator() {
6572
return createHealthIndicator(this.couchbaseOperations);
6673
}
6774

75+
@Override
76+
protected CouchbaseHealthIndicator createHealthIndicator(
77+
CouchbaseOperations couchbaseOperations) {
78+
return new CouchbaseHealthIndicator(couchbaseOperations,
79+
this.properties.getTimeout());
80+
}
81+
6882
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2012-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.autoconfigure.couchbase;
18+
19+
import java.time.Duration;
20+
21+
import org.springframework.boot.actuate.couchbase.CouchbaseHealthIndicator;
22+
import org.springframework.boot.context.properties.ConfigurationProperties;
23+
24+
/**
25+
* Configuration properties for {@link CouchbaseHealthIndicator}.
26+
*
27+
* @author Stephane Nicoll
28+
* @since 2.0.5
29+
*/
30+
@ConfigurationProperties(prefix = "management.health.couchbase")
31+
public class CouchbaseHealthIndicatorProperties {
32+
33+
/**
34+
* Timeout for getting the Bucket information from the server.
35+
*/
36+
private Duration timeout = Duration.ofMillis(1000);
37+
38+
public Duration getTimeout() {
39+
return this.timeout;
40+
}
41+
42+
public void setTimeout(Duration timeout) {
43+
this.timeout = timeout;
44+
}
45+
46+
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/couchbase/CouchbaseHealthIndicatorAutoConfigurationTests.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2017 the original author or authors.
2+
* Copyright 2012-2018 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.
@@ -27,6 +27,7 @@
2727
import org.springframework.context.annotation.Bean;
2828
import org.springframework.context.annotation.Configuration;
2929
import org.springframework.data.couchbase.core.CouchbaseOperations;
30+
import org.springframework.test.util.ReflectionTestUtils;
3031

3132
import static org.assertj.core.api.Assertions.assertThat;
3233
import static org.mockito.Mockito.mock;
@@ -35,6 +36,7 @@
3536
* Tests for {@link CouchbaseHealthIndicatorAutoConfiguration}.
3637
*
3738
* @author Phillip Webb
39+
* @author Stephane Nicoll
3840
*/
3941
public class CouchbaseHealthIndicatorAutoConfigurationTests {
4042

@@ -50,6 +52,17 @@ public void runShouldCreateIndicator() {
5052
.doesNotHaveBean(ApplicationHealthIndicator.class));
5153
}
5254

55+
@Test
56+
public void runWithCustomTimeoutShouldCreateIndicator() {
57+
this.contextRunner.withPropertyValues("management.health.couchbase.timeout=2s")
58+
.run((context) -> {
59+
assertThat(context).hasSingleBean(CouchbaseHealthIndicator.class);
60+
assertThat(ReflectionTestUtils.getField(
61+
context.getBean(CouchbaseHealthIndicator.class), "timeout"))
62+
.isEqualTo(2000L);
63+
});
64+
}
65+
5366
@Test
5467
public void runWhenDisabledShouldNotCreateIndicator() {
5568
this.contextRunner.withPropertyValues("management.health.couchbase.enabled:false")

spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/couchbase/CouchbaseHealthIndicator.java

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@
1616

1717
package org.springframework.boot.actuate.couchbase;
1818

19+
import java.time.Duration;
20+
import java.util.concurrent.TimeUnit;
21+
import java.util.concurrent.TimeoutException;
22+
1923
import com.couchbase.client.java.bucket.BucketInfo;
2024
import com.couchbase.client.java.cluster.ClusterInfo;
2125

@@ -35,26 +39,57 @@
3539
*/
3640
public class CouchbaseHealthIndicator extends AbstractHealthIndicator {
3741

38-
private CouchbaseOperations operations;
42+
private final CouchbaseOperations operations;
3943

40-
public CouchbaseHealthIndicator() {
41-
super("Couchbase health check failed");
42-
}
44+
private final long timeout;
4345

44-
public CouchbaseHealthIndicator(CouchbaseOperations couchbaseOperations) {
46+
/**
47+
* Create an indicator with the specified {@link CouchbaseOperations} and
48+
* {@code timeout}.
49+
* @param couchbaseOperations the couchbase operations
50+
* @param timeout the request timeout
51+
*/
52+
public CouchbaseHealthIndicator(CouchbaseOperations couchbaseOperations,
53+
Duration timeout) {
4554
super("Couchbase health check failed");
4655
Assert.notNull(couchbaseOperations, "CouchbaseOperations must not be null");
56+
Assert.notNull(timeout, "Timeout must not be null");
4757
this.operations = couchbaseOperations;
58+
this.timeout = timeout.toMillis();
59+
}
60+
61+
/**
62+
* Create an indicator with the specified {@link CouchbaseOperations}.
63+
* @param couchbaseOperations the couchbase operations
64+
* @deprecated as of 2.0.5 in favour of
65+
* {@link #CouchbaseHealthIndicator(CouchbaseOperations, Duration)}
66+
*/
67+
@Deprecated
68+
public CouchbaseHealthIndicator(CouchbaseOperations couchbaseOperations) {
69+
this(couchbaseOperations, Duration.ofSeconds(1));
4870
}
4971

5072
@Override
5173
protected void doHealthCheck(Health.Builder builder) throws Exception {
5274
ClusterInfo cluster = this.operations.getCouchbaseClusterInfo();
53-
BucketInfo bucket = this.operations.getCouchbaseBucket().bucketManager().info();
75+
BucketInfo bucket = getBucketInfo();
5476
String versions = StringUtils
5577
.collectionToCommaDelimitedString(cluster.getAllVersions());
5678
String nodes = StringUtils.collectionToCommaDelimitedString(bucket.nodeList());
5779
builder.up().withDetail("versions", versions).withDetail("nodes", nodes);
5880
}
5981

82+
private BucketInfo getBucketInfo() throws Exception {
83+
try {
84+
return this.operations.getCouchbaseBucket().bucketManager().info(this.timeout,
85+
TimeUnit.MILLISECONDS);
86+
}
87+
catch (RuntimeException ex) {
88+
if (ex.getCause() instanceof TimeoutException) {
89+
throw (TimeoutException) ex.getCause();
90+
}
91+
throw ex;
92+
}
93+
}
94+
6095
}

spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/couchbase/CouchbaseHealthIndicatorTests.java

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2017 the original author or authors.
2+
* Copyright 2012-2018 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.
@@ -18,7 +18,10 @@
1818

1919
import java.net.InetAddress;
2020
import java.net.UnknownHostException;
21+
import java.time.Duration;
2122
import java.util.Collections;
23+
import java.util.concurrent.TimeUnit;
24+
import java.util.concurrent.TimeoutException;
2225

2326
import com.couchbase.client.java.Bucket;
2427
import com.couchbase.client.java.bucket.BucketInfo;
@@ -51,7 +54,7 @@ public void couchbaseIsUp() throws UnknownHostException {
5154
given(bucketInfo.nodeList()).willReturn(
5255
Collections.singletonList(InetAddress.getByName("127.0.0.1")));
5356
BucketManager bucketManager = mock(BucketManager.class);
54-
given(bucketManager.info()).willReturn(bucketInfo);
57+
given(bucketManager.info(2000, TimeUnit.MILLISECONDS)).willReturn(bucketInfo);
5558
Bucket bucket = mock(Bucket.class);
5659
given(bucket.bucketManager()).willReturn(bucketManager);
5760
ClusterInfo clusterInfo = mock(ClusterInfo.class);
@@ -61,7 +64,7 @@ public void couchbaseIsUp() throws UnknownHostException {
6164
given(couchbaseOperations.getCouchbaseBucket()).willReturn(bucket);
6265
given(couchbaseOperations.getCouchbaseClusterInfo()).willReturn(clusterInfo);
6366
CouchbaseHealthIndicator healthIndicator = new CouchbaseHealthIndicator(
64-
couchbaseOperations);
67+
couchbaseOperations, Duration.ofSeconds(2));
6568
Health health = healthIndicator.health();
6669
assertThat(health.getStatus()).isEqualTo(Status.UP);
6770
assertThat(health.getDetails()).containsOnly(entry("versions", "1.2.3"),
@@ -70,13 +73,29 @@ public void couchbaseIsUp() throws UnknownHostException {
7073
verify(bucketInfo).nodeList();
7174
}
7275

76+
@Test
77+
public void couchbaseTimeout() {
78+
BucketManager bucketManager = mock(BucketManager.class);
79+
given(bucketManager.info(1500, TimeUnit.MILLISECONDS)).willThrow(
80+
new RuntimeException(new TimeoutException("timeout, expected")));
81+
Bucket bucket = mock(Bucket.class);
82+
given(bucket.bucketManager()).willReturn(bucketManager);
83+
CouchbaseOperations couchbaseOperations = mock(CouchbaseOperations.class);
84+
given(couchbaseOperations.getCouchbaseBucket()).willReturn(bucket);
85+
CouchbaseHealthIndicator healthIndicator = new CouchbaseHealthIndicator(
86+
couchbaseOperations, Duration.ofMillis(1500));
87+
Health health = healthIndicator.health();
88+
assertThat((String) health.getDetails().get("error"))
89+
.contains("timeout, expected");
90+
}
91+
7392
@Test
7493
public void couchbaseIsDown() {
7594
CouchbaseOperations couchbaseOperations = mock(CouchbaseOperations.class);
7695
given(couchbaseOperations.getCouchbaseClusterInfo())
7796
.willThrow(new IllegalStateException("test, expected"));
7897
CouchbaseHealthIndicator healthIndicator = new CouchbaseHealthIndicator(
79-
couchbaseOperations);
98+
couchbaseOperations, Duration.ofSeconds(1));
8099
Health health = healthIndicator.health();
81100
assertThat(health.getStatus()).isEqualTo(Status.DOWN);
82101
assertThat((String) health.getDetails().get("error")).contains("test, expected");

spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1276,6 +1276,7 @@ content into your application. Rather, pick only the properties that you need.
12761276
management.health.db.enabled=true # Whether to enable database health check.
12771277
management.health.cassandra.enabled=true # Whether to enable Cassandra health check.
12781278
management.health.couchbase.enabled=true # Whether to enable Couchbase health check.
1279+
management.health.couchbase.timeout=1000ms # Timeout for getting the Bucket information from the server.
12791280
management.health.defaults.enabled=true # Whether to enable default health indicators.
12801281
management.health.diskspace.enabled=true # Whether to enable disk space health check.
12811282
management.health.diskspace.path= # Path used to compute the available disk space.

0 commit comments

Comments
 (0)