Skip to content

Commit 159b80e

Browse files
committed
[grid] Getting the Grid status from the Model
To get the status we were usually querying via HTTP all nodes and then rendering that status update. This could lead to a more precise status but it will flood the Grid with HTTP requests everytime the status endpoint is queried (which some users do to monitor the Grid). On the other hand, we keep the Node status updated with a small delta from the "real time" status. This commit leverages that update process and uses the data collected there to show it to the user as the overall Grid status.
1 parent 8c4050f commit 159b80e

File tree

2 files changed

+25
-115
lines changed

2 files changed

+25
-115
lines changed

java/server/src/org/openqa/selenium/grid/router/GridStatusHandler.java

Lines changed: 23 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -18,61 +18,40 @@
1818
package org.openqa.selenium.grid.router;
1919

2020
import com.google.common.collect.ImmutableMap;
21+
2122
import org.openqa.selenium.grid.data.DistributorStatus;
2223
import org.openqa.selenium.grid.distributor.Distributor;
2324
import org.openqa.selenium.internal.Require;
24-
import org.openqa.selenium.json.Json;
25-
import org.openqa.selenium.remote.http.HttpClient;
2625
import org.openqa.selenium.remote.http.HttpHandler;
2726
import org.openqa.selenium.remote.http.HttpRequest;
2827
import org.openqa.selenium.remote.http.HttpResponse;
2928
import org.openqa.selenium.remote.tracing.AttributeKey;
3029
import org.openqa.selenium.remote.tracing.EventAttribute;
3130
import org.openqa.selenium.remote.tracing.EventAttributeValue;
32-
import org.openqa.selenium.remote.tracing.HttpTracing;
3331
import org.openqa.selenium.remote.tracing.Span;
3432
import org.openqa.selenium.remote.tracing.Status;
3533
import org.openqa.selenium.remote.tracing.Tracer;
3634

37-
import java.io.IOException;
3835
import java.util.HashMap;
3936
import java.util.List;
4037
import java.util.Map;
41-
import java.util.concurrent.CompletableFuture;
4238
import java.util.concurrent.ExecutionException;
4339
import java.util.concurrent.ExecutorService;
4440
import java.util.concurrent.Executors;
45-
import java.util.concurrent.Future;
46-
import java.util.concurrent.ScheduledExecutorService;
4741
import java.util.concurrent.TimeoutException;
4842

49-
import static java.util.concurrent.TimeUnit.MILLISECONDS;
5043
import static java.util.concurrent.TimeUnit.SECONDS;
5144
import static java.util.stream.Collectors.toList;
52-
import static org.openqa.selenium.json.Json.MAP_TYPE;
5345
import static org.openqa.selenium.remote.http.Contents.asJson;
54-
import static org.openqa.selenium.remote.http.Contents.string;
55-
import static org.openqa.selenium.remote.http.HttpMethod.GET;
5646
import static org.openqa.selenium.remote.tracing.HttpTracing.newSpanAsChildOf;
5747
import static org.openqa.selenium.remote.tracing.Tags.EXCEPTION;
58-
import static org.openqa.selenium.remote.tracing.Tags.HTTP_RESPONSE;
59-
import static org.openqa.selenium.remote.tracing.Tags.HTTP_RESPONSE_EVENT;
6048
import static org.openqa.selenium.remote.tracing.Tags.HTTP_REQUEST;
6149
import static org.openqa.selenium.remote.tracing.Tags.HTTP_REQUEST_EVENT;
50+
import static org.openqa.selenium.remote.tracing.Tags.HTTP_RESPONSE;
51+
import static org.openqa.selenium.remote.tracing.Tags.HTTP_RESPONSE_EVENT;
6252

6353
class GridStatusHandler implements HttpHandler {
6454

65-
private static final ScheduledExecutorService
66-
SCHEDULED_SERVICE =
67-
Executors.newScheduledThreadPool(
68-
1,
69-
r -> {
70-
Thread thread = new Thread(r, "Scheduled grid status executor");
71-
thread.setDaemon(true);
72-
return thread;
73-
});
74-
75-
7655
private static final ExecutorService EXECUTOR_SERVICE = Executors.newCachedThreadPool(
7756
r -> {
7857
Thread thread = new Thread(r, "Grid status executor");
@@ -81,21 +60,16 @@ class GridStatusHandler implements HttpHandler {
8160
});
8261

8362

84-
private final Json json;
8563
private final Tracer tracer;
86-
private final HttpClient.Factory clientFactory;
8764
private final Distributor distributor;
8865

89-
GridStatusHandler(Json json, Tracer tracer, HttpClient.Factory clientFactory, Distributor distributor) {
90-
this.json = Require.nonNull("JSON encoder", json);
66+
GridStatusHandler(Tracer tracer, Distributor distributor) {
9167
this.tracer = Require.nonNull("Tracer", tracer);
92-
this.clientFactory = Require.nonNull("HTTP client factory", clientFactory);
9368
this.distributor = Require.nonNull("Distributor", distributor);
9469
}
9570

9671
@Override
9772
public HttpResponse execute(HttpRequest req) {
98-
long start = System.currentTimeMillis();
9973

10074
try (Span span = newSpanAsChildOf(tracer, req, "grid.status")) {
10175
Map<String, EventAttributeValue> attributeMap = new HashMap<>();
@@ -113,7 +87,8 @@ public HttpResponse execute(HttpRequest req) {
11387
span.setStatus(Status.CANCELLED);
11488
EXCEPTION.accept(attributeMap, e);
11589
attributeMap.put(AttributeKey.EXCEPTION_MESSAGE.getKey(),
116-
EventAttribute.setValue("Unable to get distributor status due to execution error or timeout: " + e.getMessage()));
90+
EventAttribute.setValue("Error or timeout while getting Distributor "
91+
+ "status: " + e.getMessage()));
11792
HttpResponse response = new HttpResponse().setContent(asJson(
11893
ImmutableMap.of("value", ImmutableMap.of(
11994
"ready", false,
@@ -129,7 +104,8 @@ public HttpResponse execute(HttpRequest req) {
129104
span.setStatus(Status.ABORTED);
130105
EXCEPTION.accept(attributeMap, e);
131106
attributeMap.put(AttributeKey.EXCEPTION_MESSAGE.getKey(),
132-
EventAttribute.setValue("Interruption while getting distributor status: " + e.getMessage()));
107+
EventAttribute.setValue("Interruption while getting distributor status: "
108+
+ e.getMessage()));
133109

134110
HttpResponse response = new HttpResponse().setContent(asJson(
135111
ImmutableMap.of("value", ImmutableMap.of(
@@ -141,82 +117,31 @@ public HttpResponse execute(HttpRequest req) {
141117
span.addEvent(AttributeKey.EXCEPTION_EVENT.getKey(), attributeMap);
142118

143119
Thread.currentThread().interrupt();
144-
return response;
120+
return response;
145121
}
146122

147123
boolean ready = status.hasCapacity();
148124

149-
long remaining = System.currentTimeMillis() + 2000 - start;
150-
List<Future<Map<String, Object>>> nodeResults = status.getNodes().stream()
151-
.map(node -> {
152-
ImmutableMap<String, Object> defaultResponse = ImmutableMap.of(
153-
"id", node.getId(),
154-
"uri", node.getUri(),
155-
"maxSessions", node.getMaxSessionCount(),
156-
"slots", node.getSlots(),
157-
"warning", "Unable to read data from node.");
158-
159-
CompletableFuture<Map<String, Object>> toReturn = new CompletableFuture<>();
160-
161-
Future<?> future = EXECUTOR_SERVICE.submit(
162-
() -> {
163-
try (HttpClient client = clientFactory.createClient(node.getUri().toURL())) {
164-
HttpRequest nodeStatusReq = new HttpRequest(GET, "/se/grid/node/status");
165-
HttpTracing.inject(tracer, span, nodeStatusReq);
166-
HttpResponse res = client.execute(nodeStatusReq);
167-
168-
toReturn.complete(res.getStatus() == 200
169-
? json.toType(string(res), MAP_TYPE)
170-
: defaultResponse);
171-
} catch (IOException e) {
172-
toReturn.complete(defaultResponse);
173-
}
174-
});
175-
176-
SCHEDULED_SERVICE.schedule(
177-
() -> {
178-
if (!toReturn.isDone()) {
179-
toReturn.complete(defaultResponse);
180-
future.cancel(true);
181-
}
182-
},
183-
remaining,
184-
MILLISECONDS);
185-
186-
return toReturn;
187-
})
125+
List<Map<String, Object>> nodeResults = status.getNodes().stream()
126+
.map(node -> new ImmutableMap.Builder<String, Object>()
127+
.put("id", node.getId())
128+
.put("uri", node.getUri())
129+
.put("maxSessions", node.getMaxSessionCount())
130+
.put("osInfo", node.getOsInfo())
131+
.put("heartbeatPeriod", node.heartbeatPeriod().toMillis())
132+
.put("availability", node.getAvailability())
133+
.put("version", node.getVersion())
134+
.put("slots", node.getSlots())
135+
.build())
188136
.collect(toList());
189137

190138
ImmutableMap.Builder<String, Object> value = ImmutableMap.builder();
191139
value.put("ready", ready);
192140
value.put("message", ready ? "Selenium Grid ready." : "Selenium Grid not ready.");
141+
value.put("nodes", nodeResults);
193142

194-
value.put("nodes", nodeResults.stream()
195-
.map(summary -> {
196-
try {
197-
return summary.get();
198-
} catch (ExecutionException e) {
199-
span.setAttribute("error", true);
200-
span.setStatus(Status.NOT_FOUND);
201-
EXCEPTION.accept(attributeMap, e);
202-
attributeMap.put(AttributeKey.EXCEPTION_MESSAGE.getKey(),
203-
EventAttribute.setValue("Unable to get Node information: " + e.getMessage()));
204-
span.addEvent(AttributeKey.EXCEPTION_EVENT.getKey(), attributeMap);
205-
throw wrap(e);
206-
} catch (InterruptedException e) {
207-
span.setAttribute("error", true);
208-
span.setStatus(Status.NOT_FOUND);
209-
EXCEPTION.accept(attributeMap, e);
210-
attributeMap.put(AttributeKey.EXCEPTION_MESSAGE.getKey(),
211-
EventAttribute.setValue("Unable to get Node information: " + e.getMessage()));
212-
span.addEvent(AttributeKey.EXCEPTION_EVENT.getKey(), attributeMap);
213-
Thread.currentThread().interrupt();
214-
throw wrap(e);
215-
}
216-
})
217-
.collect(toList()));
218-
219-
HttpResponse res = new HttpResponse().setContent(asJson(ImmutableMap.of("value", value.build())));
143+
HttpResponse res = new HttpResponse()
144+
.setContent(asJson(ImmutableMap.of("value", value.build())));
220145
HTTP_RESPONSE.accept(span, res);
221146
HTTP_RESPONSE_EVENT.accept(attributeMap, res);
222147
attributeMap.put("grid.status", EventAttribute.setValue(ready));
@@ -225,18 +150,4 @@ public HttpResponse execute(HttpRequest req) {
225150
return res;
226151
}
227152
}
228-
229-
private RuntimeException wrap(Exception e) {
230-
if (e instanceof InterruptedException) {
231-
Thread.currentThread().interrupt();
232-
return new RuntimeException(e);
233-
}
234-
235-
Throwable cause = e.getCause();
236-
if (cause == null) {
237-
return e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e);
238-
}
239-
return cause instanceof RuntimeException ? (RuntimeException) cause
240-
: new RuntimeException(cause);
241-
}
242153
}

java/server/src/org/openqa/selenium/grid/router/Router.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@
1818
package org.openqa.selenium.grid.router;
1919

2020
import com.google.common.collect.ImmutableSet;
21+
2122
import org.openqa.selenium.grid.distributor.Distributor;
2223
import org.openqa.selenium.grid.sessionmap.SessionMap;
2324
import org.openqa.selenium.grid.sessionqueue.NewSessionQueuer;
2425
import org.openqa.selenium.internal.Require;
25-
import org.openqa.selenium.json.Json;
2626
import org.openqa.selenium.remote.http.HttpClient;
2727
import org.openqa.selenium.remote.http.HttpRequest;
2828
import org.openqa.selenium.remote.http.HttpResponse;
@@ -62,8 +62,7 @@ public Router(
6262

6363
routes =
6464
combine(
65-
get("/status")
66-
.to(() -> new GridStatusHandler(new Json(), tracer, clientFactory, distributor)),
65+
get("/status").to(() -> new GridStatusHandler(tracer, distributor)),
6766
sessions.with(new SpanDecorator(tracer, req -> "session_map")),
6867
queuer.with(new SpanDecorator(tracer, req -> "session_queuer")),
6968
distributor.with(new SpanDecorator(tracer, req -> "distributor")),

0 commit comments

Comments
 (0)