Skip to content

Commit

Permalink
Address increased build time RSS caused by certificate hot reload
Browse files Browse the repository at this point in the history
This commit resolves an issue where the build time RSS increased after implementing the certificate hot reload feature. While the exact cause wasn't pinpointed, several potential culprits were addressed. These include removing the usage of the Mutiny API (regrettably), converting a record class to a regular class, and modifying a Java Stream API usage that relied on method references.
  • Loading branch information
cescoffier committed Feb 27, 2024
1 parent f2e37d3 commit ccb3778
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
import java.net.URL;
import java.nio.file.Files;
import java.security.cert.X509Certificate;
import java.time.Duration;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import javax.net.ssl.SSLHandshakeException;

Expand Down Expand Up @@ -89,7 +91,7 @@ public class MainHttpServerTlsCertificateReloadTest {
File certs;

@Test
void test() throws IOException {
void test() throws IOException, ExecutionException, InterruptedException, TimeoutException {
var options = new HttpClientOptions()
.setSsl(true)
.setDefaultPort(url.getPort())
Expand All @@ -110,7 +112,7 @@ void test() throws IOException {
new File(certs, "/tls.key").toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING);

// Trigger the reload
TlsCertificateReloader.reload().await().atMost(Duration.ofSeconds(10));
TlsCertificateReloader.reload().toCompletableFuture().get(10, TimeUnit.SECONDS);

// The client truststore is not updated, thus it should fail.
assertThatThrownBy(() -> vertx.createHttpClient(options)
Expand All @@ -133,7 +135,7 @@ void test() throws IOException {
assertThat(response1).isNotEqualTo(response2); // Because cert duration are different.

// Trigger another reload
TlsCertificateReloader.reload().await().atMost(Duration.ofSeconds(10));
TlsCertificateReloader.reload().toCompletableFuture().get(10, TimeUnit.SECONDS);

var response3 = vertx.createHttpClient(options2)
.request(HttpMethod.GET, "/hello")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
import java.net.URL;
import java.nio.file.Files;
import java.security.cert.X509Certificate;
import java.time.Duration;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import javax.net.ssl.SSLHandshakeException;

Expand Down Expand Up @@ -82,7 +84,7 @@ public class MainHttpServerTlsPKCS12CertificateReloadTest {
File certs;

@Test
void test() throws IOException {
void test() throws IOException, ExecutionException, InterruptedException, TimeoutException {
var options = new HttpClientOptions()
.setSsl(true)
.setDefaultPort(url.getPort())
Expand All @@ -102,7 +104,7 @@ void test() throws IOException {
new File(certs, "/tls.p12").toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING);

// Trigger the reload
TlsCertificateReloader.reload().await().atMost(Duration.ofSeconds(10));
TlsCertificateReloader.reload().toCompletableFuture().get(10, TimeUnit.SECONDS);

// The client truststore is not updated, thus it should fail.
assertThatThrownBy(() -> vertx.createHttpClient(options)
Expand All @@ -126,7 +128,7 @@ void test() throws IOException {
assertThat(response1).isNotEqualTo(response2); // Because cert duration are different.

// Trigger another reload
TlsCertificateReloader.reload().await().atMost(Duration.ofSeconds(10));
TlsCertificateReloader.reload().toCompletableFuture().get(10, TimeUnit.SECONDS);

var response3 = vertx.createHttpClient(options2)
.request(HttpMethod.GET, "/hello")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
import java.io.IOException;
import java.nio.file.Files;
import java.security.cert.X509Certificate;
import java.time.Duration;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;

import javax.net.ssl.SSLHandshakeException;
Expand Down Expand Up @@ -118,7 +120,7 @@ public void execute(BuildContext context) {
File certs;

@Test
void test() throws IOException {
void test() throws IOException, ExecutionException, InterruptedException, TimeoutException {
var options = new HttpClientOptions()
.setSsl(true)
.setDefaultPort(9001) // Management interface test port
Expand All @@ -139,7 +141,7 @@ void test() throws IOException {
new File(certs, "/tls.key").toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING);

// Trigger the reload
TlsCertificateReloader.reload().await().atMost(Duration.ofSeconds(10));
TlsCertificateReloader.reload().toCompletableFuture().get(10, TimeUnit.SECONDS);

// The client truststore is not updated, thus it should fail.
assertThatThrownBy(() -> vertx.createHttpClient(options)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import org.jboss.logging.Logger;

import io.quarkus.vertx.http.runtime.ServerSslConfig;
import io.smallrye.mutiny.Uni;
import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Handler;
Expand Down Expand Up @@ -62,9 +63,9 @@ public static long initCertReloadingAction(Vertx vertx, HttpServer server,
return -1;
}

Supplier<Uni<Boolean>> task = new Supplier<Uni<Boolean>>() {
Supplier<CompletionStage<Boolean>> task = new Supplier<CompletionStage<Boolean>>() {
@Override
public Uni<Boolean> get() {
public CompletionStage<Boolean> get() {

Future<Boolean> future = vertx.executeBlocking(new Callable<SSLOptions>() {
@Override
Expand Down Expand Up @@ -101,15 +102,14 @@ public void handle(AsyncResult<Boolean> ar) {
}
});

return Uni.createFrom().completionStage(future.toCompletionStage());
return future.toCompletionStage();
}
};

long id = vertx.setPeriodic(configuration.certificate.reloadPeriod.get().toMillis(), new Handler<Long>() {
long id = vertx.setPeriodic(period, new Handler<Long>() {
@Override
public void handle(Long id) {
//noinspection ResultOfMethodCallIgnored
task.get().subscribeAsCompletionStage();
task.get();
}
});

Expand All @@ -133,9 +133,14 @@ public static void unschedule(Vertx vertx, long id) {
*
* @return a Uni that is completed when all the reload tasks have been executed
*/
public static Uni<Void> reload() {
var unis = TASKS.stream().map(ReloadCertificateTask::action).map(Supplier::get).collect(Collectors.toList());
return Uni.join().all(unis).andFailFast().replaceWithVoid();
public static CompletionStage<Void> reload() {
@SuppressWarnings("rawtypes")
CompletableFuture[] futures = new CompletableFuture[TASKS.size()];
for (int i = 0; i < TASKS.size(); i++) {
futures[i] = TASKS.get(i).action().get().toCompletableFuture();
}

return CompletableFuture.allOf(futures);
}

private static SSLOptions reloadFileContent(SSLOptions ssl, ServerSslConfig configuration) throws IOException {
Expand Down Expand Up @@ -183,7 +188,45 @@ private static SSLOptions reloadFileContent(SSLOptions ssl, ServerSslConfig conf
return copy;
}

record ReloadCertificateTask(long it, Supplier<Uni<Boolean>> action) {
static final class ReloadCertificateTask {
private final long it;
private final Supplier<CompletionStage<Boolean>> action;

ReloadCertificateTask(long it, Supplier<CompletionStage<Boolean>> action) {
this.it = it;
this.action = action;
}

public long it() {
return it;
}

public Supplier<CompletionStage<Boolean>> action() {
return action;
}

@Override
public boolean equals(Object obj) {
if (obj == this)
return true;
if (obj == null || obj.getClass() != this.getClass())
return false;
var that = (ReloadCertificateTask) obj;
return this.it == that.it &&
Objects.equals(this.action, that.action);
}

@Override
public int hashCode() {
return Objects.hash(it, action);
}

@Override
public String toString() {
return "ReloadCertificateTask[" +
"it=" + it + ", " +
"action=" + action + ']';
}

}
}

0 comments on commit ccb3778

Please sign in to comment.