Skip to content

Commit 521b43c

Browse files
authored
Fix/status update trigger (#2719)
1 parent 1007351 commit 521b43c

File tree

5 files changed

+37
-30
lines changed

5 files changed

+37
-30
lines changed

spring-boot-admin-server/src/main/java/de/codecentric/boot/admin/server/services/AbstractEventHandler.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import reactor.core.publisher.Flux;
2727
import reactor.core.scheduler.Scheduler;
2828
import reactor.core.scheduler.Schedulers;
29-
import reactor.util.retry.Retry;
3029

3130
import de.codecentric.boot.admin.server.domain.events.InstanceEvent;
3231

@@ -58,7 +57,7 @@ public void start() {
5857
.ofType(this.eventType)
5958
.cast(this.eventType)
6059
.transform(this::handle)
61-
.retryWhen(Retry.indefinitely().doBeforeRetry((s) -> this.log.warn("Unexpected error", s.failure())))
60+
.onErrorContinue((throwable, o) -> this.log.warn("Unexpected error", throwable))
6261
.subscribe();
6362
}
6463

spring-boot-admin-server/src/main/java/de/codecentric/boot/admin/server/services/IntervalCheck.java

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@
2323
import java.util.function.Function;
2424
import java.util.logging.Level;
2525

26-
import org.slf4j.Logger;
27-
import org.slf4j.LoggerFactory;
26+
import lombok.Getter;
27+
import lombok.Setter;
28+
import lombok.extern.slf4j.Slf4j;
2829
import org.springframework.lang.Nullable;
2930
import reactor.core.Disposable;
3031
import reactor.core.publisher.Flux;
@@ -42,18 +43,20 @@
4243
*
4344
* @author Johannes Edmeier
4445
*/
46+
@Slf4j
4547
public class IntervalCheck {
4648

47-
private static final Logger log = LoggerFactory.getLogger(IntervalCheck.class);
48-
4949
private final String name;
5050

5151
private final Map<InstanceId, Instant> lastChecked = new ConcurrentHashMap<>();
5252

5353
private final Function<InstanceId, Mono<Void>> checkFn;
5454

55+
@Getter
56+
@Setter
5557
private Duration interval;
5658

59+
@Setter
5760
private Duration minRetention;
5861

5962
@Nullable
@@ -83,7 +86,7 @@ public void start() {
8386
.concatMap((i) -> this.checkAllInstances())
8487
.retryWhen(Retry.backoff(Long.MAX_VALUE, Duration.ofSeconds(1))
8588
.doBeforeRetry((s) -> log.warn("Unexpected error in {}-check", this.name, s.failure())))
86-
.subscribe();
89+
.subscribe(null, (error) -> log.error("Unexpected error in {}-check", name, error));
8790
}
8891

8992
public void markAsChecked(InstanceId instanceId) {
@@ -94,7 +97,7 @@ protected Mono<Void> checkAllInstances() {
9497
log.debug("check {} for all instances", this.name);
9598
Instant expiration = Instant.now().minus(this.minRetention);
9699
return Flux.fromIterable(this.lastChecked.entrySet())
97-
.filter((e) -> e.getValue().isBefore(expiration))
100+
.filter((entry) -> entry.getValue().isBefore(expiration))
98101
.map(Map.Entry::getKey)
99102
.flatMap(this.checkFn)
100103
.then();
@@ -111,12 +114,4 @@ public void stop() {
111114
}
112115
}
113116

114-
public void setInterval(Duration interval) {
115-
this.interval = interval;
116-
}
117-
118-
public void setMinRetention(Duration minRetention) {
119-
this.minRetention = minRetention;
120-
}
121-
122117
}

spring-boot-admin-server/src/main/java/de/codecentric/boot/admin/server/services/StatusUpdateTrigger.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,13 @@ protected Publisher<Void> handle(Flux<InstanceEvent> publisher) {
5252
}
5353

5454
protected Mono<Void> updateStatus(InstanceId instanceId) {
55-
return this.statusUpdater.updateStatus(instanceId).onErrorResume((e) -> {
56-
log.warn("Unexpected error while updating status for {}", instanceId, e);
57-
return Mono.empty();
58-
}).doFinally((s) -> this.intervalCheck.markAsChecked(instanceId));
55+
return this.statusUpdater.timeout(this.intervalCheck.getInterval())
56+
.updateStatus(instanceId)
57+
.onErrorResume((e) -> {
58+
log.warn("Unexpected error while updating status for {}", instanceId, e);
59+
return Mono.empty();
60+
})
61+
.doFinally((s) -> this.intervalCheck.markAsChecked(instanceId));
5962
}
6063

6164
@Override

spring-boot-admin-server/src/main/java/de/codecentric/boot/admin/server/services/StatusUpdater.java

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,15 @@
1616

1717
package de.codecentric.boot.admin.server.services;
1818

19+
import java.time.Duration;
1920
import java.util.HashMap;
2021
import java.util.LinkedHashMap;
2122
import java.util.Map;
2223
import java.util.Objects;
2324
import java.util.logging.Level;
2425

25-
import org.slf4j.Logger;
26-
import org.slf4j.LoggerFactory;
26+
import lombok.RequiredArgsConstructor;
27+
import lombok.extern.slf4j.Slf4j;
2728
import org.springframework.core.ParameterizedTypeReference;
2829
import org.springframework.http.HttpStatus;
2930
import org.springframework.http.HttpStatusCode;
@@ -46,10 +47,10 @@
4647
*
4748
* @author Johannes Edmeier
4849
*/
50+
@Slf4j
51+
@RequiredArgsConstructor
4952
public class StatusUpdater {
5053

51-
private static final Logger log = LoggerFactory.getLogger(StatusUpdater.class);
52-
5354
private static final ParameterizedTypeReference<Map<String, Object>> RESPONSE_TYPE = new ParameterizedTypeReference<>() {
5455
};
5556

@@ -59,16 +60,15 @@ public class StatusUpdater {
5960

6061
private final ApiMediaTypeHandler apiMediaTypeHandler;
6162

62-
public StatusUpdater(InstanceRepository repository, InstanceWebClient instanceWebClient,
63-
ApiMediaTypeHandler apiMediaTypeHandler) {
64-
this.repository = repository;
65-
this.instanceWebClient = instanceWebClient;
66-
this.apiMediaTypeHandler = apiMediaTypeHandler;
63+
private Duration timeout = Duration.ofSeconds(10);
64+
65+
public StatusUpdater timeout(Duration timeout) {
66+
this.timeout = timeout;
67+
return this;
6768
}
6869

6970
public Mono<Void> updateStatus(InstanceId id) {
7071
return this.repository.computeIfPresent(id, (key, instance) -> this.doUpdateStatus(instance)).then();
71-
7272
}
7373

7474
protected Mono<Instance> doUpdateStatus(Instance instance) {
@@ -82,11 +82,20 @@ protected Mono<Instance> doUpdateStatus(Instance instance) {
8282
.uri(Endpoint.HEALTH)
8383
.exchangeToMono(this::convertStatusInfo)
8484
.log(log.getName(), Level.FINEST)
85+
.timeout(getTimeoutWithMargin())
8586
.doOnError((ex) -> logError(instance, ex))
8687
.onErrorResume(this::handleError)
8788
.map(instance::withStatusInfo);
8889
}
8990

91+
/*
92+
* return a timeout less than the given one to prevent backdrops in concurrent get
93+
* request. This prevents flakyness of health checks.
94+
*/
95+
private Duration getTimeoutWithMargin() {
96+
return this.timeout.minusSeconds(1).abs();
97+
}
98+
9099
protected Mono<StatusInfo> convertStatusInfo(ClientResponse response) {
91100
boolean hasCompatibleContentType = response.headers()
92101
.contentType()

spring-boot-admin-server/src/test/java/de/codecentric/boot/admin/server/services/StatusUpdateTriggerTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ public class StatusUpdateTriggerTest {
5656
@BeforeEach
5757
public void setUp() throws Exception {
5858
when(this.updater.updateStatus(any(InstanceId.class))).thenReturn(Mono.empty());
59+
when(this.updater.timeout(any())).thenReturn(this.updater);
5960

6061
this.trigger = new StatusUpdateTrigger(this.updater, this.events.flux());
6162
this.trigger.start();

0 commit comments

Comments
 (0)