Skip to content

Commit

Permalink
Close other HTTP connections on reload
Browse files Browse the repository at this point in the history
This will prevent old persistent connections hanging onto the old
shutdown application.

Fixes quarkusio#18776

(cherry picked from commit 8394756)
  • Loading branch information
stuartwdouglas authored and gsmet committed Aug 3, 2021
1 parent 876e928 commit dcc0fa4
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public class DevConsoleManager {
private static volatile Map<String, Map<String, Object>> templateInfo;
private static volatile HotReplacementContext hotReplacementContext;
private static volatile Object quarkusBootstrap;
private static volatile boolean doingHttpInitiatedReload;
/**
* Global map that can be used to share data betweeen the runtime and deployment side
* to enable communication.
Expand Down Expand Up @@ -80,6 +81,14 @@ public static <T> T getGlobal(String name) {
return (T) globals.get(name);
}

public static boolean isDoingHttpInitiatedReload() {
return doingHttpInitiatedReload;
}

public static void setDoingHttpInitiatedReload(boolean doingHttpInitiatedReload) {
DevConsoleManager.doingHttpInitiatedReload = doingHttpInitiatedReload;
}

public static void close() {
handler = null;
templateInfo = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,13 @@ protected void handlePost(RoutingContext event, MultiMap form) throws Exception
writer.newLine();
}
}

DevConsoleManager.getHotReplacementContext().doScan(true);
//if we don't set this the connection will be killed on restart
DevConsoleManager.setDoingHttpInitiatedReload(true);
try {
DevConsoleManager.getHotReplacementContext().doScan(true);
} finally {
DevConsoleManager.setDoingHttpInitiatedReload(false);
}
flashMessage(event, "Configuration updated");
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
import io.quarkus.vertx.core.runtime.config.VertxConfiguration;
import io.quarkus.vertx.http.runtime.HttpConfiguration.InsecureRequests;
import io.quarkus.vertx.http.runtime.devmode.RemoteSyncHandler;
import io.quarkus.vertx.http.runtime.devmode.VertxHttpHotReplacementSetup;
import io.quarkus.vertx.http.runtime.filters.Filter;
import io.quarkus.vertx.http.runtime.filters.Filters;
import io.quarkus.vertx.http.runtime.filters.GracefulShutdownFilter;
Expand Down Expand Up @@ -168,6 +169,7 @@ public static void shutDownDevMode() {
}
rootHandler = null;
hotReplacementHandler = null;

}

public static void startServerAfterFailedStart() {
Expand Down Expand Up @@ -250,6 +252,13 @@ public void startServer(Supplier<Vertx> vertx, ShutdownContext shutdown,
websocketSubProtocols, auxiliaryApplication);
if (launchMode != LaunchMode.DEVELOPMENT) {
shutdown.addShutdownTask(closeTask);
} else {
shutdown.addShutdownTask(new Runnable() {
@Override
public void run() {
VertxHttpHotReplacementSetup.handleDevModeRestart();
}
});
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
package io.quarkus.vertx.http.runtime.devmode;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import io.quarkus.dev.ErrorPageGenerators;
import io.quarkus.dev.console.DevConsoleManager;
import io.quarkus.dev.spi.HotReplacementContext;
import io.quarkus.dev.spi.HotReplacementSetup;
import io.quarkus.vertx.http.runtime.VertxHttpRecorder;
Expand Down Expand Up @@ -39,7 +45,38 @@ public void handleFailedInitialStart() {
VertxHttpRecorder.startServerAfterFailedStart();
}

private static volatile Set<ConnectionBase> openConnections;

public static void handleDevModeRestart() {
if (DevConsoleManager.isDoingHttpInitiatedReload()) {
return;
}
Set<ConnectionBase> cons = VertxHttpHotReplacementSetup.openConnections;
if (cons != null) {
for (ConnectionBase con : cons) {
con.close();
}
}
}

void handleHotReplacementRequest(RoutingContext routingContext) {
if (openConnections == null) {
synchronized (VertxHttpHotReplacementSetup.class) {
if (openConnections == null) {
openConnections = Collections.newSetFromMap(new ConcurrentHashMap<>());
}
}
}
ConnectionBase connectionBase = (ConnectionBase) routingContext.request().connection();
if (openConnections.add(connectionBase)) {
connectionBase.closeFuture().onComplete(new Handler<AsyncResult<Void>>() {
@Override
public void handle(AsyncResult<Void> event) {
openConnections.remove(connectionBase);
}
});
}

if ((nextUpdate > System.currentTimeMillis() && !hotReplacementContext.isTest())
|| routingContext.request().headers().contains(HEADER_NAME)) {
if (hotReplacementContext.getDeploymentProblem() != null) {
Expand All @@ -50,27 +87,42 @@ void handleHotReplacementRequest(RoutingContext routingContext) {
return;
}
ClassLoader current = Thread.currentThread().getContextClassLoader();
ConnectionBase connectionBase = (ConnectionBase) routingContext.request().connection();
connectionBase.getContext().executeBlocking(new Handler<Promise<Boolean>>() {
@Override
public void handle(Promise<Boolean> event) {
//the blocking pool may have a stale TCCL
Thread.currentThread().setContextClassLoader(current);
boolean restart = false;
synchronized (this) {
if (nextUpdate < System.currentTimeMillis() || hotReplacementContext.isTest()) {
nextUpdate = System.currentTimeMillis() + HOT_REPLACEMENT_INTERVAL;
try {
restart = hotReplacementContext.doScan(true);
} catch (Exception e) {
event.fail(new IllegalStateException("Unable to perform live reload scanning", e));
return;
try {
DevConsoleManager.setDoingHttpInitiatedReload(true);
synchronized (this) {
if (nextUpdate < System.currentTimeMillis() || hotReplacementContext.isTest()) {
nextUpdate = System.currentTimeMillis() + HOT_REPLACEMENT_INTERVAL;
try {
restart = hotReplacementContext.doScan(true);
} catch (Exception e) {
event.fail(new IllegalStateException("Unable to perform live reload scanning", e));
return;
}
}
}
}
if (hotReplacementContext.getDeploymentProblem() != null) {
event.fail(hotReplacementContext.getDeploymentProblem());
return;
if (hotReplacementContext.getDeploymentProblem() != null) {
event.fail(hotReplacementContext.getDeploymentProblem());
return;
}
if (restart) {
//close all connections on close, except for this one
//this prevents long running requests such as SSE or websockets
//from holding onto the old deployment
Set<ConnectionBase> connections = new HashSet<>(openConnections);
for (ConnectionBase con : connections) {
if (con != connectionBase) {
con.close();
}
}
}
} finally {
DevConsoleManager.setDoingHttpInitiatedReload(false);
}
event.complete(restart);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
Expand Down Expand Up @@ -62,6 +63,8 @@ public class CuratedApplication implements Serializable, AutoCloseable {

final AppModel appModel;

final AtomicInteger runtimeClassLoaderCount = new AtomicInteger();

CuratedApplication(QuarkusBootstrap quarkusBootstrap, CurationResult curationResult,
ConfiguredClassLoading configuredClassLoading) {
this.quarkusBootstrap = quarkusBootstrap;
Expand Down Expand Up @@ -330,7 +333,9 @@ public QuarkusClassLoader createRuntimeClassLoader(Map<String, byte[]> resources
public QuarkusClassLoader createRuntimeClassLoader(ClassLoader base, Map<String, byte[]> resources,
Map<String, byte[]> transformedClasses) {
QuarkusClassLoader.Builder builder = QuarkusClassLoader
.builder("Quarkus Runtime ClassLoader: " + quarkusBootstrap.getMode(),
.builder(
"Quarkus Runtime ClassLoader: " + quarkusBootstrap.getMode() + " restart no:"
+ runtimeClassLoaderCount.getAndIncrement(),
getBaseRuntimeClassLoader(), false)
.setAssertionsEnabled(quarkusBootstrap.isAssertionsEnabled())
.setAggregateParentResources(true);
Expand Down

0 comments on commit dcc0fa4

Please sign in to comment.