Skip to content

Commit 9d58c75

Browse files
pujaganidiemol
andauthored
[grid] Extract Node session creation to use Either to help retry sessions accurately (#9168)
* [grid] Make LocalNode communicate whether to retry session request or not. * [grid] Use WebDriverException to encapsulate errors from Node. Extract session creation in One Shot node to use Either. * [grid] Small text changes Co-authored-by: Diego Molina <[email protected]> Co-authored-by: Diego Molina <[email protected]>
1 parent 4258b53 commit 9d58c75

File tree

5 files changed

+49
-23
lines changed

5 files changed

+49
-23
lines changed
+1-3
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,7 @@
1515
// specific language governing permissions and limitations
1616
// under the License.
1717

18-
package org.openqa.selenium.grid.distributor;
19-
20-
import org.openqa.selenium.SessionNotCreatedException;
18+
package org.openqa.selenium;
2119

2220
public class RetrySessionRequestException extends SessionNotCreatedException {
2321
public RetrySessionRequestException(String msg) {

java/server/src/org/openqa/selenium/grid/distributor/Distributor.java

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.google.common.collect.ImmutableMap;
2121
import com.google.common.collect.ImmutableSet;
2222
import org.openqa.selenium.Capabilities;
23+
import org.openqa.selenium.RetrySessionRequestException;
2324
import org.openqa.selenium.SessionNotCreatedException;
2425
import org.openqa.selenium.grid.data.CreateSessionRequest;
2526
import org.openqa.selenium.grid.data.CreateSessionResponse;

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.openqa.selenium.Beta;
2727
import org.openqa.selenium.Capabilities;
2828
import org.openqa.selenium.ImmutableCapabilities;
29+
import org.openqa.selenium.RetrySessionRequestException;
2930
import org.openqa.selenium.SessionNotCreatedException;
3031
import org.openqa.selenium.concurrent.Regularly;
3132
import org.openqa.selenium.events.EventBus;
@@ -48,7 +49,6 @@
4849
import org.openqa.selenium.grid.data.Slot;
4950
import org.openqa.selenium.grid.data.SlotId;
5051
import org.openqa.selenium.grid.distributor.Distributor;
51-
import org.openqa.selenium.grid.distributor.RetrySessionRequestException;
5252
import org.openqa.selenium.grid.distributor.selector.DefaultSlotSelector;
5353
import org.openqa.selenium.grid.log.LoggingOptions;
5454
import org.openqa.selenium.grid.node.HealthCheck;

java/server/src/org/openqa/selenium/grid/node/k8s/OneShotNode.java

+15-3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.openqa.selenium.NoSuchSessionException;
2525
import org.openqa.selenium.PersistentCapabilities;
2626
import org.openqa.selenium.WebDriver;
27+
import org.openqa.selenium.WebDriverException;
2728
import org.openqa.selenium.WebDriverInfo;
2829
import org.openqa.selenium.events.EventBus;
2930
import org.openqa.selenium.grid.config.Config;
@@ -46,6 +47,7 @@
4647
import org.openqa.selenium.grid.security.SecretOptions;
4748
import org.openqa.selenium.grid.server.BaseServerOptions;
4849
import org.openqa.selenium.grid.server.EventBusOptions;
50+
import org.openqa.selenium.internal.Either;
4951
import org.openqa.selenium.internal.Require;
5052
import org.openqa.selenium.json.Json;
5153
import org.openqa.selenium.remote.CommandExecutor;
@@ -154,18 +156,28 @@ public static Node create(Config config) {
154156

155157
@Override
156158
public Optional<CreateSessionResponse> newSession(CreateSessionRequest sessionRequest) {
159+
Either<WebDriverException, CreateSessionResponse> result = createNewSession(sessionRequest);
160+
161+
if (result.isRight()) {
162+
return Optional.of(result.right());
163+
} else {
164+
return Optional.empty();
165+
}
166+
}
167+
168+
public Either<WebDriverException, CreateSessionResponse> createNewSession(CreateSessionRequest sessionRequest) {
157169
if (driver != null) {
158170
throw new IllegalStateException("Only expected one session at a time");
159171
}
160172

161173
Optional<WebDriver> driver = driverInfo.createDriver(sessionRequest.getCapabilities());
162174
if (!driver.isPresent()) {
163-
return Optional.empty();
175+
return Either.left(new WebDriverException("Unable to create a driver instance"));
164176
}
165177

166178
if (!(driver.get() instanceof RemoteWebDriver)) {
167179
driver.get().quit();
168-
return Optional.empty();
180+
return Either.left(new WebDriverException("Driver is not a RemoteWebDriver instance"));
169181
}
170182

171183
this.driver = (RemoteWebDriver) driver.get();
@@ -181,7 +193,7 @@ public Optional<CreateSessionResponse> newSession(CreateSessionRequest sessionRe
181193

182194
events.fire(new NodeDrainStarted(getId()));
183195

184-
return Optional.of(
196+
return Either.right(
185197
new CreateSessionResponse(
186198
getSession(sessionId),
187199
JSON.toJson(ImmutableMap.of(

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

+31-16
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
import org.openqa.selenium.ImmutableCapabilities;
2929
import org.openqa.selenium.NoSuchSessionException;
3030
import org.openqa.selenium.PersistentCapabilities;
31+
import org.openqa.selenium.RetrySessionRequestException;
32+
import org.openqa.selenium.SessionNotCreatedException;
3133
import org.openqa.selenium.WebDriverException;
3234
import org.openqa.selenium.concurrent.Regularly;
3335
import org.openqa.selenium.events.EventBus;
@@ -42,21 +44,22 @@
4244
import org.openqa.selenium.grid.data.SessionClosedEvent;
4345
import org.openqa.selenium.grid.data.Slot;
4446
import org.openqa.selenium.grid.data.SlotId;
47+
import org.openqa.selenium.grid.jmx.JMXHelper;
48+
import org.openqa.selenium.grid.jmx.ManagedAttribute;
49+
import org.openqa.selenium.grid.jmx.ManagedService;
4550
import org.openqa.selenium.grid.node.ActiveSession;
4651
import org.openqa.selenium.grid.node.HealthCheck;
4752
import org.openqa.selenium.grid.node.Node;
4853
import org.openqa.selenium.grid.node.SessionFactory;
4954
import org.openqa.selenium.grid.security.Secret;
55+
import org.openqa.selenium.internal.Either;
5056
import org.openqa.selenium.internal.Require;
5157
import org.openqa.selenium.io.TemporaryFilesystem;
5258
import org.openqa.selenium.io.Zip;
5359
import org.openqa.selenium.json.Json;
5460
import org.openqa.selenium.remote.SessionId;
5561
import org.openqa.selenium.remote.http.HttpRequest;
5662
import org.openqa.selenium.remote.http.HttpResponse;
57-
import org.openqa.selenium.grid.jmx.JMXHelper;
58-
import org.openqa.selenium.grid.jmx.ManagedAttribute;
59-
import org.openqa.selenium.grid.jmx.ManagedService;
6063
import org.openqa.selenium.remote.tracing.AttributeKey;
6164
import org.openqa.selenium.remote.tracing.EventAttribute;
6265
import org.openqa.selenium.remote.tracing.EventAttributeValue;
@@ -252,17 +255,27 @@ public boolean isSupporting(Capabilities capabilities) {
252255

253256
@Override
254257
public Optional<CreateSessionResponse> newSession(CreateSessionRequest sessionRequest) {
258+
Either<WebDriverException, CreateSessionResponse> result = createNewSession(sessionRequest);
259+
260+
if (result.isRight()) {
261+
return Optional.of(result.right());
262+
} else {
263+
return Optional.empty();
264+
}
265+
}
266+
267+
public Either<WebDriverException, CreateSessionResponse> createNewSession(CreateSessionRequest sessionRequest) {
255268
Require.nonNull("Session request", sessionRequest);
256269

257270
try (Span span = tracer.getCurrentContext().createSpan("node.new_session")) {
258271
Map<String, EventAttributeValue> attributeMap = new HashMap<>();
259272
attributeMap
260-
.put(AttributeKey.LOGGER_CLASS.getKey(), EventAttribute.setValue(getClass().getName()));
261-
LOG.fine("Creating new session using span: " + span);
273+
.put(AttributeKey.LOGGER_CLASS.getKey(), EventAttribute.setValue(getClass().getName()));
262274
attributeMap.put("session.request.capabilities",
263-
EventAttribute.setValue(sessionRequest.getCapabilities().toString()));
275+
EventAttribute.setValue(sessionRequest.getCapabilities().toString()));
264276
attributeMap.put("session.request.downstreamdialect",
265-
EventAttribute.setValue(sessionRequest.getDownstreamDialects().toString()));
277+
EventAttribute.setValue(sessionRequest.getDownstreamDialects().toString()));
278+
266279
int currentSessionCount = getCurrentSessionCount();
267280
span.setAttribute("current.session.count", currentSessionCount);
268281
attributeMap.put("current.session.count", EventAttribute.setValue(currentSessionCount));
@@ -272,11 +285,12 @@ public Optional<CreateSessionResponse> newSession(CreateSessionRequest sessionRe
272285
span.setStatus(Status.RESOURCE_EXHAUSTED);
273286
attributeMap.put("max.session.count", EventAttribute.setValue(maxSessionCount));
274287
span.addEvent("Max session count reached", attributeMap);
275-
return Optional.empty();
288+
return Either.left(new RetrySessionRequestException("Max session count reached."));
276289
}
277290
if (isDraining()) {
278291
span.setStatus(Status.UNAVAILABLE.withDescription("The node is draining. Cannot accept new sessions."));
279-
return Optional.empty();
292+
return Either.left(
293+
new RetrySessionRequestException("The node is draining. Cannot accept new sessions."));
280294
}
281295

282296
// Identify possible slots to use as quickly as possible to enable concurrent session starting
@@ -296,8 +310,9 @@ public Optional<CreateSessionResponse> newSession(CreateSessionRequest sessionRe
296310
if (slotToUse == null) {
297311
span.setAttribute("error", true);
298312
span.setStatus(Status.NOT_FOUND);
299-
span.addEvent("No slot matched capabilities ", attributeMap);
300-
return Optional.empty();
313+
span.addEvent("No slot matched the requested capabilities. All slots are busy.", attributeMap);
314+
return Either.left(
315+
new RetrySessionRequestException("No slot matched the requested capabilities. All slots are busy."));
301316
}
302317

303318
Optional<ActiveSession> possibleSession = slotToUse.apply(sessionRequest);
@@ -306,8 +321,8 @@ public Optional<CreateSessionResponse> newSession(CreateSessionRequest sessionRe
306321
slotToUse.release();
307322
span.setAttribute("error", true);
308323
span.setStatus(Status.NOT_FOUND);
309-
span.addEvent("No slots available for capabilities ", attributeMap);
310-
return Optional.empty();
324+
span.addEvent("Unable to create session with the driver", attributeMap);
325+
return Either.left(new SessionNotCreatedException("Unable to create session with the driver"));
311326
}
312327

313328
ActiveSession session = possibleSession.get();
@@ -327,9 +342,9 @@ public Optional<CreateSessionResponse> newSession(CreateSessionRequest sessionRe
327342
// The session we return has to look like it came from the node, since we might be dealing
328343
// with a webdriver implementation that only accepts connections from localhost
329344
Session externalSession = createExternalSession(session, externalUri);
330-
return Optional.of(new CreateSessionResponse(
331-
externalSession,
332-
getEncoder(session.getDownstreamDialect()).apply(externalSession)));
345+
return Either.right(new CreateSessionResponse(
346+
externalSession,
347+
getEncoder(session.getDownstreamDialect()).apply(externalSession)));
333348
}
334349
}
335350

0 commit comments

Comments
 (0)