Skip to content

Commit 9b9e2ff

Browse files
Update health check strategy (#795)
Separte DataFeedLoop health check from the other components, set the global status to out of service only if DataFeedLoop is down. In this case, when calling healtch check endpoint, the resposne http code is 500, otherwise if any other components is down, set the global status to "WARNING", the http code of the response stays as 200. This will avoid unncessary BDK Bot restarting when other components are not healthy.
1 parent 59b78cb commit 9b9e2ff

File tree

4 files changed

+26
-78
lines changed

4 files changed

+26
-78
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
package com.symphony.bdk.app.spring.config;
22

33
import com.symphony.bdk.app.spring.service.SymphonyBdkHealthIndicator;
4-
import com.symphony.bdk.app.spring.service.SymphonyBdkHealthIndicator.CustomStatusCodeMapper;
54
import com.symphony.bdk.core.service.health.HealthService;
65

7-
import org.springframework.boot.actuate.health.HttpCodeStatusMapper;
8-
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
96
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
107
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
118
import org.springframework.context.annotation.Bean;
@@ -20,9 +17,4 @@ public SymphonyBdkHealthIndicator symphonyBdkHealthIndicator(HealthService healt
2017
return new SymphonyBdkHealthIndicator(healthService);
2118
}
2219

23-
@Bean
24-
@ConditionalOnBean(name = "bot")
25-
public HttpCodeStatusMapper customStatusCodeMapper() {
26-
return new CustomStatusCodeMapper();
27-
}
2820
}

symphony-bdk-spring/symphony-bdk-app-spring-boot-starter/src/main/java/com/symphony/bdk/app/spring/service/SymphonyBdkHealthIndicator.java

+8-36
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import org.apiguardian.api.API;
1313
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
1414
import org.springframework.boot.actuate.health.Health;
15-
import org.springframework.boot.actuate.health.HttpCodeStatusMapper;
1615
import org.springframework.boot.actuate.health.Status;
1716

1817
import java.util.Map;
@@ -70,10 +69,14 @@ private void buildHealthDetail(Health.Builder builder, V3Health health) {
7069
V3HealthStatus agtStatus = users.get(AGT).getStatus();
7170
V3HealthStatus datafeedLoop = healthService.datafeedHealthCheck();
7271

73-
boolean global = podStatus == V3HealthStatus.UP && dfStatus == V3HealthStatus.UP && kmStatus == V3HealthStatus.UP
74-
&& agtStatus == V3HealthStatus.UP && datafeedLoop == V3HealthStatus.UP;
75-
76-
builder.status(global ? Status.UP.getCode() : WARNING)
72+
if (datafeedLoop != V3HealthStatus.UP) {
73+
builder.status(Status.OUT_OF_SERVICE);
74+
} else {
75+
boolean global = podStatus == V3HealthStatus.UP && dfStatus == V3HealthStatus.UP && kmStatus == V3HealthStatus.UP
76+
&& agtStatus == V3HealthStatus.UP;
77+
builder.status(global ? Status.UP.getCode() : WARNING);
78+
}
79+
builder
7780
.withDetail(POD, services.get(POD))
7881
.withDetail(DF, services.get(DF))
7982
.withDetail(KM, services.get(KM))
@@ -91,35 +94,4 @@ private void buildHealthDownDetail(Health.Builder builder) {
9194
.withDetail(CE, DOWN)
9295
.withDetail(DFL, DOWN);
9396
}
94-
95-
96-
/**
97-
* A custom spring actuator health endpoint status code mapper.
98-
*
99-
* It will return 500 for all status except Status.UP
100-
*/
101-
@API(status = API.Status.INTERNAL)
102-
public static class CustomStatusCodeMapper implements HttpCodeStatusMapper {
103-
104-
@Override
105-
public int getStatusCode(Status status) {
106-
if (status == Status.DOWN) {
107-
return 500;
108-
}
109-
110-
if (status == Status.OUT_OF_SERVICE) {
111-
return 503;
112-
}
113-
114-
if (status == Status.UNKNOWN) {
115-
return 500;
116-
}
117-
118-
if (status.getCode().equals(WARNING)) {
119-
return 500;
120-
}
121-
122-
return 200;
123-
}
124-
}
12597
}

symphony-bdk-spring/symphony-bdk-app-spring-boot-starter/src/test/java/com/symphony/bdk/app/spring/config/BdkHealthIndicatorConfigTest.java

+1-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import org.junit.jupiter.api.Test;
66

7-
import static org.junit.jupiter.api.Assertions.*;
7+
import static org.junit.jupiter.api.Assertions.assertNotNull;
88
import static org.mockito.Mockito.mock;
99

1010
class BdkHealthIndicatorConfigTest {
@@ -15,7 +15,5 @@ void createSymphonyBdkHealthIndicatorTest() {
1515
final HealthService healthService = mock(HealthService.class);
1616

1717
assertNotNull(config.symphonyBdkHealthIndicator(healthService));
18-
19-
assertNotNull(config.customStatusCodeMapper());
2018
}
2119
}

symphony-bdk-spring/symphony-bdk-app-spring-boot-starter/src/test/java/com/symphony/bdk/app/spring/service/SymphonyBdkHealthIndicatorTest.java

+17-31
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,25 @@
11
package com.symphony.bdk.app.spring.service;
22

3-
import com.symphony.bdk.app.spring.service.SymphonyBdkHealthIndicator.CustomStatusCodeMapper;
43
import com.symphony.bdk.core.service.health.HealthService;
54
import com.symphony.bdk.gen.api.model.V3Health;
65
import com.symphony.bdk.gen.api.model.V3HealthComponent;
76
import com.symphony.bdk.gen.api.model.V3HealthStatus;
87
import com.symphony.bdk.http.api.ApiException;
98
import com.symphony.bdk.http.api.ApiRuntimeException;
109

11-
import org.junit.jupiter.api.Nested;
1210
import org.junit.jupiter.api.Test;
1311
import org.junit.jupiter.api.extension.ExtendWith;
1412
import org.junit.jupiter.params.ParameterizedTest;
1513
import org.junit.jupiter.params.provider.Arguments;
16-
import org.junit.jupiter.params.provider.CsvSource;
1714
import org.junit.jupiter.params.provider.MethodSource;
1815
import org.mockito.InjectMocks;
1916
import org.mockito.Mock;
2017
import org.mockito.junit.jupiter.MockitoExtension;
2118
import org.springframework.boot.actuate.health.Health;
22-
import org.springframework.boot.actuate.health.HttpCodeStatusMapper;
2319
import org.springframework.boot.actuate.health.Status;
2420

2521
import java.util.Collections;
22+
import java.util.Map;
2623
import java.util.stream.Stream;
2724

2825
import static org.assertj.core.api.Assertions.assertThat;
@@ -34,20 +31,29 @@ class SymphonyBdkHealthIndicatorTest {
3431
@Mock HealthService healthService;
3532
@InjectMocks SymphonyBdkHealthIndicator healthIndicator;
3633

37-
@Test
38-
void doHealthCheck_successful() throws Exception {
34+
@ParameterizedTest
35+
@MethodSource("datafeedHealthTestArguments")
36+
void doHealthCheck_datafeedStatus_expectedStatusCode(V3HealthStatus dfl, V3HealthStatus df, String globalCode) throws Exception {
3937
V3Health health = new V3Health();
4038
health.putServicesItem("pod", new V3HealthComponent().status(V3HealthStatus.UP));
41-
health.putServicesItem("datafeed", new V3HealthComponent().status(V3HealthStatus.UP));
39+
health.putServicesItem("datafeed", new V3HealthComponent().status(df));
4240
health.putServicesItem("key_manager", new V3HealthComponent().status(V3HealthStatus.UP));
4341
health.putUsersItem("agentservice", new V3HealthComponent().status(V3HealthStatus.UP));
4442
health.putUsersItem("ceservice", new V3HealthComponent().status(V3HealthStatus.UP));
4543
when(healthService.healthCheckExtended()).thenReturn(health);
46-
when(healthService.datafeedHealthCheck()).thenReturn(V3HealthStatus.UP);
44+
when(healthService.datafeedHealthCheck()).thenReturn(dfl);
4745
Health.Builder builder = new Health.Builder();
4846
healthIndicator.doHealthCheck(builder);
4947
Health build = builder.build();
50-
assertThat(build.getStatus().getCode()).isEqualTo("UP");
48+
assertThat(build.getStatus().getCode()).isEqualTo(globalCode);
49+
}
50+
51+
private static Stream<Arguments> datafeedHealthTestArguments() {
52+
return Stream.of(
53+
Arguments.of(V3HealthStatus.UP, V3HealthStatus.UP, Status.UP.getCode()),
54+
Arguments.of(V3HealthStatus.DOWN, V3HealthStatus.UP, Status.OUT_OF_SERVICE.getCode()),
55+
Arguments.of(V3HealthStatus.UP, V3HealthStatus.DOWN, "WARNING")
56+
);
5157
}
5258

5359
@Test
@@ -78,7 +84,7 @@ void doHealthCheck_exception() throws Exception {
7884
+ " \"message\": \"Ceservice authentication credentials missing or misconfigured\"\n" + " }\n"
7985
+ " }\n" + "}";
8086

81-
doThrow(new ApiRuntimeException(new ApiException(503, "message", Collections.EMPTY_MAP, body))).when(healthService)
87+
doThrow(new ApiRuntimeException(new ApiException(503, "message", Map.of(), body))).when(healthService)
8288
.healthCheckExtended();
8389
when(healthService.datafeedHealthCheck()).thenReturn(V3HealthStatus.UP);
8490
Health.Builder builder = new Health.Builder();
@@ -92,32 +98,12 @@ void doHealthCheck_badGw_exception() throws Exception {
9298
final String body = "<html>\n" + "<head><title>502 Bad Gateway</title></head>\n" + "<body>\n"
9399
+ "<center><h1>502 Bad Gateway</h1></center>\n" + "</body>\n" + "</html>";
94100

95-
doThrow(new ApiRuntimeException(new ApiException(502, "message", Collections.EMPTY_MAP, body))).when(healthService)
101+
doThrow(new ApiRuntimeException(new ApiException(502, "message", Map.of(), body))).when(healthService)
96102
.healthCheckExtended();
97103
Health.Builder builder = new Health.Builder();
98104
healthIndicator.doHealthCheck(builder);
99105
Health build = builder.build();
100106
assertThat(build.getStatus().getCode()).isEqualTo("DOWN");
101107
}
102108

103-
@Nested
104-
class CustomStatusCodeMapperTest {
105-
106-
HttpCodeStatusMapper mapper = new CustomStatusCodeMapper();
107-
108-
@ParameterizedTest
109-
@MethodSource("getStatusArguments")
110-
void getStatusCode_status_code(Status status, int code) {
111-
assertThat(mapper.getStatusCode(status)).isEqualTo(code);
112-
}
113-
114-
private static Stream<Arguments> getStatusArguments() {
115-
return Stream.of(
116-
Arguments.of(Status.UP, 200),
117-
Arguments.of(Status.DOWN, 500),
118-
Arguments.of(new Status("WARNING"), 500),
119-
Arguments.of(Status.OUT_OF_SERVICE, 503),
120-
Arguments.of(Status.UNKNOWN, 500));
121-
}
122-
}
123109
}

0 commit comments

Comments
 (0)