Skip to content

Commit

Permalink
Use lazy values to initialized HealthSupport FT handlers (#5147)
Browse files Browse the repository at this point in the history
  • Loading branch information
spericas authored Oct 11, 2022
1 parent 14eb43d commit 7c09327
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Flow;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;

Expand Down Expand Up @@ -58,6 +59,7 @@ public final class FaultTolerance {
new AtomicReference<>();
private static final AtomicReference<LazyValue<ExecutorService>> EXECUTOR = new AtomicReference<>();
private static final AtomicReference<Config> CONFIG = new AtomicReference<>(Config.empty());
private static final AtomicBoolean INITIALIZED = new AtomicBoolean(false);

static {
SCHEDULED_EXECUTOR.set(LazyValue.create(ScheduledThreadPoolSupplier.builder()
Expand Down Expand Up @@ -106,13 +108,25 @@ public static void scheduledExecutor(Supplier<? extends ScheduledExecutorService
}

static LazyValue<? extends ExecutorService> executor() {
INITIALIZED.set(true);
return EXECUTOR.get();
}

static LazyValue<? extends ScheduledExecutorService> scheduledExecutor() {
INITIALIZED.set(true);
return SCHEDULED_EXECUTOR.get();
}

/**
* Mostly used for testing. This class is considered to be initialized if any of its
* (lazy valued) executors were returned.
*
* @return boolean indicating whether init took place
*/
public static boolean initialized() {
return INITIALIZED.get();
}

/**
* A builder to configure a customized sequence of fault tolerance handlers.
*
Expand Down
35 changes: 35 additions & 0 deletions reactive/health/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,39 @@
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<executions>
<execution>
<id>default-test</id>
<goals>
<goal>test</goal>
</goals>
<phase>test</phase>
<configuration>
<excludes>
<exclude>**/HealthSupportInitTest.java</exclude>
</excludes>
</configuration>
</execution>
<execution>
<id>health-support-init-test</id>
<goals>
<goal>test</goal>
</goals>
<phase>test</phase>
<configuration>
<includes>
<include>**/HealthSupportInitTest.java</include>
</includes>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import java.util.logging.Logger;
import java.util.stream.Collectors;

import io.helidon.common.LazyValue;
import io.helidon.common.http.Http;
import io.helidon.common.reactive.Single;
import io.helidon.config.Config;
Expand Down Expand Up @@ -87,8 +88,8 @@ public final class HealthSupport extends HelidonRestServiceSupport {
private final Set<String> includedHealthChecks;
private final Set<String> excludedHealthChecks;
private final MessageBodyWriter<JsonStructure> jsonpWriter = JsonpSupport.writer();
private final Timeout timeout;
private final Async async;
private final LazyValue<? extends Timeout> timeout;
private final LazyValue<? extends Async> async;

private HealthSupport(Builder builder) {
super(LOGGER, builder, SERVICE_NAME);
Expand All @@ -111,9 +112,9 @@ private HealthSupport(Builder builder) {
this.excludedHealthChecks = Collections.emptySet();
}


this.timeout = Timeout.create(Duration.ofMillis(builder.timeoutMillis));
this.async = Async.create();
// Lazy values to prevent early init of maybe-not-yet-configured FT thread pools
this.timeout = LazyValue.create(() -> Timeout.create(Duration.ofMillis(builder.timeoutMillis)));
this.async = LazyValue.create(Async::create);
}

@Override
Expand Down Expand Up @@ -186,7 +187,8 @@ private void head(ServerResponse res, Collection<io.helidon.health.HealthCheck>

void invoke(ServerResponse res, Collection<io.helidon.health.HealthCheck> healthChecks, boolean sendDetails) {
// timeout on the asynchronous execution
Single<HealthHttpResponse> result = timeout.invoke(() -> async.invoke(() -> callHealthChecks(healthChecks)));
Single<HealthHttpResponse> result = timeout.get().invoke(
() -> async.get().invoke(() -> callHealthChecks(healthChecks)));

// handle timeouts and failures in execution
result = result.onErrorResume(throwable -> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (c) 2022 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.helidon.reactive.health;

import io.helidon.reactive.faulttolerance.FaultTolerance;
import org.junit.jupiter.api.Test;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.notNullValue;

public class HealthSupportInitTest {

/**
* Must be executed at startup, preferably in its own VM. Verifies that fault tolerance
* is not initialized too early due to execution of health extension.
*/
@Test
void checkLazyFaultToleranceInitialization() {
HealthSupport support = HealthSupport.create();
assertThat(support, notNullValue());
assertThat(FaultTolerance.initialized(), is(false));
}
}

0 comments on commit 7c09327

Please sign in to comment.