Skip to content

Commit 0338677

Browse files
committed
[grid] shutdown the server backend on stop
1 parent bf22ba0 commit 0338677

File tree

15 files changed

+200
-38
lines changed

15 files changed

+200
-38
lines changed

java/src/org/openqa/selenium/grid/TemplateGridServerCommand.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
package org.openqa.selenium.grid;
1919

20+
import java.io.Closeable;
2021
import java.util.Arrays;
2122
import java.util.Collections;
2223
import java.util.List;
@@ -47,7 +48,17 @@ public Server<?> asServer(Config initialConfig) {
4748
Handlers handler = createHandlers(config);
4849

4950
return new NettyServer(
50-
new BaseServerOptions(config), handler.httpHandler, handler.websocketHandler);
51+
new BaseServerOptions(config), handler.httpHandler, handler.websocketHandler) {
52+
53+
@Override
54+
public void stop() {
55+
try {
56+
handler.close();
57+
} finally {
58+
super.stop();
59+
}
60+
}
61+
};
5162
}
5263

5364
private static final String GRAPHQL = "/graphql";
@@ -77,7 +88,7 @@ protected static Routable baseRoute(String prefix, Route route) {
7788

7889
protected abstract Handlers createHandlers(Config config);
7990

80-
public static class Handlers {
91+
public abstract static class Handlers implements Closeable {
8192
public final HttpHandler httpHandler;
8293
public final BiFunction<String, Consumer<Message>, Optional<Consumer<Message>>>
8394
websocketHandler;
@@ -89,5 +100,8 @@ public Handlers(
89100
this.websocketHandler =
90101
websocketHandler == null ? (str, sink) -> Optional.empty() : websocketHandler;
91102
}
103+
104+
@Override
105+
public abstract void close();
92106
}
93107
}

java/src/org/openqa/selenium/grid/commands/EventBusCommand.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,17 @@ public Server<?> asServer(Config initialConfig) {
135135
return httpResponse(false, "Status checking was interrupted");
136136
}
137137
}),
138-
Route.get("/readyz").to(() -> req -> new HttpResponse().setStatus(HTTP_NO_CONTENT))));
138+
Route.get("/readyz").to(() -> req -> new HttpResponse().setStatus(HTTP_NO_CONTENT)))) {
139+
140+
@Override
141+
public void stop() {
142+
try {
143+
bus.close();
144+
} finally {
145+
super.stop();
146+
}
147+
}
148+
};
139149
}
140150

141151
@Override

java/src/org/openqa/selenium/grid/commands/Hub.java

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@
4242
import org.openqa.selenium.grid.TemplateGridServerCommand;
4343
import org.openqa.selenium.grid.config.Config;
4444
import org.openqa.selenium.grid.config.Role;
45-
import org.openqa.selenium.grid.distributor.Distributor;
4645
import org.openqa.selenium.grid.distributor.config.DistributorOptions;
4746
import org.openqa.selenium.grid.distributor.local.LocalDistributor;
4847
import org.openqa.selenium.grid.graphql.GraphqlHandler;
@@ -57,9 +56,7 @@
5756
import org.openqa.selenium.grid.server.EventBusOptions;
5857
import org.openqa.selenium.grid.server.NetworkOptions;
5958
import org.openqa.selenium.grid.server.Server;
60-
import org.openqa.selenium.grid.sessionmap.SessionMap;
6159
import org.openqa.selenium.grid.sessionmap.local.LocalSessionMap;
62-
import org.openqa.selenium.grid.sessionqueue.NewSessionQueue;
6360
import org.openqa.selenium.grid.sessionqueue.config.NewSessionQueueOptions;
6461
import org.openqa.selenium.grid.sessionqueue.local.LocalNewSessionQueue;
6562
import org.openqa.selenium.grid.web.CombinedHandler;
@@ -120,7 +117,7 @@ protected Handlers createHandlers(Config config) {
120117

121118
CombinedHandler handler = new CombinedHandler();
122119

123-
SessionMap sessions = new LocalSessionMap(tracer, bus);
120+
LocalSessionMap sessions = new LocalSessionMap(tracer, bus);
124121
handler.addHandler(sessions);
125122

126123
BaseServerOptions serverOptions = new BaseServerOptions(config);
@@ -141,7 +138,7 @@ protected Handlers createHandlers(Config config) {
141138

142139
DistributorOptions distributorOptions = new DistributorOptions(config);
143140
NewSessionQueueOptions newSessionRequestOptions = new NewSessionQueueOptions(config);
144-
NewSessionQueue queue =
141+
LocalNewSessionQueue queue =
145142
new LocalNewSessionQueue(
146143
tracer,
147144
distributorOptions.getSlotMatcher(),
@@ -151,7 +148,7 @@ protected Handlers createHandlers(Config config) {
151148
newSessionRequestOptions.getBatchSize());
152149
handler.addHandler(queue);
153150

154-
Distributor distributor =
151+
LocalDistributor distributor =
155152
new LocalDistributor(
156153
tracer,
157154
bus,
@@ -212,7 +209,15 @@ protected Handlers createHandlers(Config config) {
212209
// these checks
213210
httpHandler = combine(httpHandler, Route.get("/readyz").to(() -> readinessCheck));
214211

215-
return new Handlers(httpHandler, new ProxyWebsocketsIntoGrid(clientFactory, sessions));
212+
return new Handlers(httpHandler, new ProxyWebsocketsIntoGrid(clientFactory, sessions)) {
213+
@Override
214+
public void close() {
215+
router.close();
216+
distributor.close();
217+
queue.close();
218+
bus.close();
219+
}
220+
};
216221
}
217222

218223
@Override

java/src/org/openqa/selenium/grid/commands/Standalone.java

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@
6363
import org.openqa.selenium.grid.server.Server;
6464
import org.openqa.selenium.grid.sessionmap.SessionMap;
6565
import org.openqa.selenium.grid.sessionmap.local.LocalSessionMap;
66-
import org.openqa.selenium.grid.sessionqueue.NewSessionQueue;
6766
import org.openqa.selenium.grid.sessionqueue.config.NewSessionQueueOptions;
6867
import org.openqa.selenium.grid.sessionqueue.local.LocalNewSessionQueue;
6968
import org.openqa.selenium.grid.web.CombinedHandler;
@@ -145,7 +144,7 @@ protected Handlers createHandlers(Config config) {
145144

146145
DistributorOptions distributorOptions = new DistributorOptions(config);
147146
NewSessionQueueOptions newSessionRequestOptions = new NewSessionQueueOptions(config);
148-
NewSessionQueue queue =
147+
LocalNewSessionQueue queue =
149148
new LocalNewSessionQueue(
150149
tracer,
151150
distributorOptions.getSlotMatcher(),
@@ -155,7 +154,7 @@ protected Handlers createHandlers(Config config) {
155154
newSessionRequestOptions.getBatchSize());
156155
combinedHandler.addHandler(queue);
157156

158-
Distributor distributor =
157+
LocalDistributor distributor =
159158
new LocalDistributor(
160159
tracer,
161160
bus,
@@ -171,9 +170,8 @@ protected Handlers createHandlers(Config config) {
171170
distributorOptions.getSlotMatcher());
172171
combinedHandler.addHandler(distributor);
173172

174-
Routable router =
175-
new Router(tracer, clientFactory, sessions, queue, distributor)
176-
.with(networkOptions.getSpecComplianceChecks());
173+
Router router = new Router(tracer, clientFactory, sessions, queue, distributor);
174+
Routable routerWithSpecChecks = router.with(networkOptions.getSpecComplianceChecks());
177175

178176
HttpHandler readinessCheck =
179177
req -> {
@@ -192,8 +190,8 @@ protected Handlers createHandlers(Config config) {
192190

193191
Routable appendRoute =
194192
Stream.of(
195-
baseRoute(subPath, combine(router)),
196-
hubRoute(subPath, combine(router)),
193+
baseRoute(subPath, combine(routerWithSpecChecks)),
194+
hubRoute(subPath, combine(routerWithSpecChecks)),
197195
graphqlRoute(subPath, () -> graphqlHandler))
198196
.reduce(Route::combine)
199197
.get();
@@ -218,7 +216,14 @@ protected Handlers createHandlers(Config config) {
218216
httpHandler = combine(httpHandler, Route.get("/readyz").to(() -> readinessCheck));
219217
Node node = createNode(config, bus, distributor, combinedHandler);
220218

221-
return new Handlers(httpHandler, new ProxyNodeWebsockets(clientFactory, node, subPath));
219+
return new Handlers(httpHandler, new ProxyNodeWebsockets(clientFactory, node, subPath)) {
220+
@Override
221+
public void close() {
222+
router.close();
223+
distributor.close();
224+
queue.close();
225+
}
226+
};
222227
}
223228

224229
@Override

java/src/org/openqa/selenium/grid/distributor/httpd/DistributorServer.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@
3131
import com.google.common.collect.ImmutableMap;
3232
import com.google.common.collect.ImmutableSet;
3333
import com.google.common.net.MediaType;
34+
import java.io.Closeable;
35+
import java.io.IOException;
36+
import java.io.UncheckedIOException;
3437
import java.util.Collections;
3538
import java.util.Set;
3639
import java.util.logging.Level;
@@ -118,7 +121,18 @@ protected Handlers createHandlers(Config config) {
118121
"message",
119122
"Distributor is ready"))))),
120123
get("/readyz").to(() -> readinessCheck)),
121-
null);
124+
null) {
125+
@Override
126+
public void close() {
127+
if (distributor instanceof Closeable) {
128+
try {
129+
((Closeable) distributor).close();
130+
} catch (IOException e) {
131+
throw new UncheckedIOException(e);
132+
}
133+
}
134+
}
135+
};
122136
}
123137

124138
@Override

java/src/org/openqa/selenium/grid/distributor/local/LocalDistributor.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
package org.openqa.selenium.grid.distributor.local;
1919

2020
import static com.google.common.collect.ImmutableSet.toImmutableSet;
21+
import static org.openqa.selenium.concurrent.ExecutorServices.shutdownGracefully;
2122
import static org.openqa.selenium.grid.data.Availability.DOWN;
2223
import static org.openqa.selenium.grid.data.Availability.DRAINING;
2324
import static org.openqa.selenium.grid.data.Availability.UP;
@@ -34,6 +35,7 @@
3435
import com.google.common.collect.ImmutableSet;
3536
import dev.failsafe.Failsafe;
3637
import dev.failsafe.RetryPolicy;
38+
import java.io.Closeable;
3739
import java.io.UncheckedIOException;
3840
import java.net.URI;
3941
import java.time.Duration;
@@ -45,7 +47,6 @@
4547
import java.util.Optional;
4648
import java.util.Set;
4749
import java.util.concurrent.ConcurrentHashMap;
48-
import java.util.concurrent.Executor;
4950
import java.util.concurrent.ExecutorService;
5051
import java.util.concurrent.Executors;
5152
import java.util.concurrent.ScheduledExecutorService;
@@ -118,7 +119,7 @@
118119
@ManagedService(
119120
objectName = "org.seleniumhq.grid:type=Distributor,name=LocalDistributor",
120121
description = "Grid 4 node distributor")
121-
public class LocalDistributor extends Distributor implements AutoCloseable {
122+
public class LocalDistributor extends Distributor implements Closeable {
122123

123124
private static final Logger LOG = Logger.getLogger(LocalDistributor.class.getName());
124125

@@ -165,7 +166,7 @@ public class LocalDistributor extends Distributor implements AutoCloseable {
165166
return thread;
166167
});
167168

168-
private final Executor sessionCreatorExecutor;
169+
private final ExecutorService sessionCreatorExecutor;
169170

170171
private final NewSessionQueue sessionQueue;
171172

@@ -752,9 +753,10 @@ public int getIdleSlots() {
752753
@Override
753754
public void close() {
754755
LOG.info("Shutting down Distributor executor service");
755-
purgeDeadNodesService.shutdown();
756-
nodeHealthCheckService.shutdown();
757-
newSessionService.shutdown();
756+
shutdownGracefully("Local Distributor - Purge Dead Nodes", purgeDeadNodesService);
757+
shutdownGracefully("Local Distributor - Node Health Check", nodeHealthCheckService);
758+
shutdownGracefully("Local Distributor - New Session Queue", newSessionService);
759+
shutdownGracefully("Local Distributor - Session Creation", sessionCreatorExecutor);
758760
}
759761

760762
private class NewSessionRunnable implements Runnable {

java/src/org/openqa/selenium/grid/node/httpd/NodeServer.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@
3030
import com.google.common.net.MediaType;
3131
import dev.failsafe.Failsafe;
3232
import dev.failsafe.RetryPolicy;
33+
import java.io.Closeable;
34+
import java.io.IOException;
35+
import java.io.UncheckedIOException;
3336
import java.util.Collections;
3437
import java.util.Set;
3538
import java.util.concurrent.ExecutorService;
@@ -172,7 +175,18 @@ protected Handlers createHandlers(Config config) {
172175
Route httpHandler = Route.combine(node, get("/readyz").to(() -> readinessCheck));
173176

174177
return new Handlers(
175-
httpHandler, new ProxyNodeWebsockets(clientFactory, node, nodeOptions.getGridSubPath()));
178+
httpHandler, new ProxyNodeWebsockets(clientFactory, node, nodeOptions.getGridSubPath())) {
179+
@Override
180+
public void close() {
181+
if (node instanceof Closeable) {
182+
try {
183+
((Closeable) node).close();
184+
} catch (IOException e) {
185+
throw new UncheckedIOException(e);
186+
}
187+
}
188+
}
189+
};
176190
}
177191

178192
@Override
@@ -225,6 +239,15 @@ public NettyServer start() {
225239

226240
return this;
227241
}
242+
243+
@Override
244+
public void stop() {
245+
try {
246+
handler.close();
247+
} finally {
248+
super.stop();
249+
}
250+
}
228251
};
229252
}
230253

java/src/org/openqa/selenium/grid/node/local/LocalNode.java

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
package org.openqa.selenium.grid.node.local;
1919

2020
import static com.google.common.collect.ImmutableSet.toImmutableSet;
21+
import static org.openqa.selenium.concurrent.ExecutorServices.shutdownGracefully;
2122
import static org.openqa.selenium.grid.data.Availability.DOWN;
2223
import static org.openqa.selenium.grid.data.Availability.DRAINING;
2324
import static org.openqa.selenium.grid.data.Availability.UP;
@@ -37,6 +38,7 @@
3738
import com.google.common.cache.RemovalNotification;
3839
import com.google.common.collect.ImmutableList;
3940
import com.google.common.collect.ImmutableMap;
41+
import java.io.Closeable;
4042
import java.io.File;
4143
import java.io.IOException;
4244
import java.io.Serializable;
@@ -114,7 +116,7 @@
114116
@ManagedService(
115117
objectName = "org.seleniumhq.grid:type=Node,name=LocalNode",
116118
description = "Node running the webdriver sessions.")
117-
public class LocalNode extends Node {
119+
public class LocalNode extends Node implements Closeable {
118120

119121
private static final Json JSON = new Json();
120122
private static final Logger LOG = Logger.getLogger(LocalNode.class.getName());
@@ -139,6 +141,7 @@ public class LocalNode extends Node {
139141
private final Cache<SessionId, UUID> sessionToDownloadsDir;
140142
private final AtomicInteger pendingSessions = new AtomicInteger();
141143
private final AtomicInteger sessionCount = new AtomicInteger();
144+
private final Runnable shutdown;
142145

143146
protected LocalNode(
144147
Tracer tracer,
@@ -301,6 +304,23 @@ protected LocalNode(
301304
heartbeatPeriod.getSeconds(),
302305
TimeUnit.SECONDS);
303306

307+
shutdown =
308+
() -> {
309+
if (heartbeatNodeService.isShutdown()) return;
310+
311+
shutdownGracefully(
312+
"Local Node - Session Cleanup " + externalUri, sessionCleanupNodeService);
313+
shutdownGracefully(
314+
"UploadTempFile Cleanup Node " + externalUri, uploadTempFileCleanupNodeService);
315+
shutdownGracefully(
316+
"DownloadTempFile Cleanup Node " + externalUri, downloadTempFileCleanupNodeService);
317+
shutdownGracefully("HeartBeat Node " + externalUri, heartbeatNodeService);
318+
319+
// ensure we do not leak running browsers
320+
currentSessions.invalidateAll();
321+
currentSessions.cleanUp();
322+
};
323+
304324
Runtime.getRuntime()
305325
.addShutdownHook(
306326
new Thread(
@@ -311,6 +331,11 @@ protected LocalNode(
311331
new JMXHelper().register(this);
312332
}
313333

334+
@Override
335+
public void close() {
336+
shutdown.run();
337+
}
338+
314339
private void stopTimedOutSession(RemovalNotification<SessionId, SessionSlot> notification) {
315340
if (notification.getKey() != null && notification.getValue() != null) {
316341
SessionSlot slot = notification.getValue();

0 commit comments

Comments
 (0)