Skip to content

Commit e7aa2c4

Browse files
authored
Merge branch 'trunk' into renovate/com.google.googlejavaformat-google-java-format-1.x
2 parents b2cf956 + e7f5524 commit e7aa2c4

File tree

12 files changed

+414
-715
lines changed

12 files changed

+414
-715
lines changed

.bazelversion

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
7.3.1
1+
7.4.1

.github/workflows/stale.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ jobs:
2121
stale-issue-message: 'This issue is stale because it has been open 280 days with no activity. Remove stale label or comment or this will be closed in 14 days.'
2222
close-issue-message: 'This issue was closed because it has been stalled for 14 days with no activity.'
2323
stale-issue-label: 'I-stale'
24-
days-before-stale: 280
24+
days-before-stale: 180
2525
days-before-close: 14
2626
- uses: actions/stale@v9
2727
with:

MODULE.bazel

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ bazel_dep(name = "contrib_rules_jvm", version = "0.27.0")
1212
bazel_dep(name = "platforms", version = "0.0.10")
1313

1414
# Required for the closure rules
15-
bazel_dep(name = "protobuf", version = "29.1", dev_dependency = True, repo_name = "com_google_protobuf")
15+
bazel_dep(name = "protobuf", version = "29.2", dev_dependency = True, repo_name = "com_google_protobuf")
1616

1717
# Required for rules_rust to import the crates properly
1818
bazel_dep(name = "rules_cc", version = "0.0.9", dev_dependency = True)

java/src/org/openqa/selenium/grid/distributor/GridModel.java

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,12 @@ public void release(SessionId id) {
400400
}
401401
}
402402

403-
public void reserve(NodeStatus status, Slot slot) {
403+
/**
404+
* A helper to reserve a slot of a node. The writeLock must be acquired outside to ensure the view
405+
* of the NodeStatus is the current state, otherwise concurrent calls to amend will work with an
406+
* outdated view of slots.
407+
*/
408+
private void reserve(NodeStatus status, Slot slot) {
404409
Instant now = Instant.now();
405410

406411
Slot reserved =
@@ -490,30 +495,29 @@ public void updateHealthCheckCount(NodeId id, Availability availability) {
490495
}
491496
}
492497

498+
/**
499+
* A helper to replace the availability and a slot of a node. The writeLock must be acquired
500+
* outside to ensure the view of the NodeStatus is the current state, otherwise concurrent calls
501+
* to amend will work with an outdated view of slots.
502+
*/
493503
private void amend(Availability availability, NodeStatus status, Slot slot) {
494504
Set<Slot> newSlots = new HashSet<>(status.getSlots());
495505
newSlots.removeIf(s -> s.getId().equals(slot.getId()));
496506
newSlots.add(slot);
497507

498508
NodeStatus node = getNode(status.getNodeId());
499509

500-
Lock writeLock = lock.writeLock();
501-
writeLock.lock();
502-
try {
503-
nodes.remove(node);
504-
nodes.add(
505-
new NodeStatus(
506-
status.getNodeId(),
507-
status.getExternalUri(),
508-
status.getMaxSessionCount(),
509-
newSlots,
510-
availability,
511-
status.getHeartbeatPeriod(),
512-
status.getSessionTimeout(),
513-
status.getVersion(),
514-
status.getOsInfo()));
515-
} finally {
516-
writeLock.unlock();
517-
}
510+
nodes.remove(node);
511+
nodes.add(
512+
new NodeStatus(
513+
status.getNodeId(),
514+
status.getExternalUri(),
515+
status.getMaxSessionCount(),
516+
newSlots,
517+
availability,
518+
status.getHeartbeatPeriod(),
519+
status.getSessionTimeout(),
520+
status.getVersion(),
521+
status.getOsInfo()));
518522
}
519523
}

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

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,7 @@ public LocalDistributor add(Node node) {
355355
// An exception occurs if Node heartbeat has started but the server is not ready.
356356
// Unhandled exception blocks the event-bus thread from processing any event henceforth.
357357
NodeStatus initialNodeStatus;
358+
Runnable healthCheck;
358359
try {
359360
initialNodeStatus = node.getStatus();
360361
if (initialNodeStatus.getAvailability() != UP) {
@@ -363,8 +364,17 @@ public LocalDistributor add(Node node) {
363364
// We do not need to add this Node for now.
364365
return this;
365366
}
366-
model.add(initialNodeStatus);
367-
nodes.put(node.getId(), node);
367+
// Extract the health check
368+
healthCheck = asRunnableHealthCheck(node);
369+
Lock writeLock = lock.writeLock();
370+
writeLock.lock();
371+
try {
372+
nodes.put(node.getId(), node);
373+
model.add(initialNodeStatus);
374+
allChecks.put(node.getId(), healthCheck);
375+
} finally {
376+
writeLock.unlock();
377+
}
368378
} catch (Exception e) {
369379
LOG.log(
370380
Debug.getDebugLogLevel(),
@@ -373,10 +383,6 @@ public LocalDistributor add(Node node) {
373383
return this;
374384
}
375385

376-
// Extract the health check
377-
Runnable healthCheck = asRunnableHealthCheck(node);
378-
allChecks.put(node.getId(), healthCheck);
379-
380386
updateNodeStatus(initialNodeStatus, healthCheck);
381387

382388
LOG.info(
@@ -415,7 +421,15 @@ private void updateNodeStatus(NodeStatus status, Runnable healthCheck) {
415421

416422
private Runnable runNodeHealthChecks() {
417423
return () -> {
418-
ImmutableMap<NodeId, Runnable> nodeHealthChecks = ImmutableMap.copyOf(allChecks);
424+
ImmutableMap<NodeId, Runnable> nodeHealthChecks;
425+
Lock readLock = this.lock.readLock();
426+
readLock.lock();
427+
try {
428+
nodeHealthChecks = ImmutableMap.copyOf(allChecks);
429+
} finally {
430+
readLock.unlock();
431+
}
432+
419433
for (Runnable nodeHealthCheck : nodeHealthChecks.values()) {
420434
GuardedRunnable.guard(nodeHealthCheck).run();
421435
}

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,8 +377,10 @@ public boolean isReady() {
377377
@VisibleForTesting
378378
@ManagedAttribute(name = "CurrentSessions")
379379
public int getCurrentSessionCount() {
380+
// we need the exact size, see javadoc of Cache.size
381+
long n = currentSessions.asMap().values().stream().count();
380382
// It seems wildly unlikely we'll overflow an int
381-
return Math.toIntExact(currentSessions.size());
383+
return Math.toIntExact(n);
382384
}
383385

384386
@VisibleForTesting
@@ -1005,6 +1007,9 @@ public HealthCheck getHealthCheck() {
10051007
public void drain() {
10061008
bus.fire(new NodeDrainStarted(getId()));
10071009
draining = true;
1010+
// Ensure the pendingSessions counter will not be decremented by timed out sessions not included
1011+
// in the currentSessionCount and the NodeDrainComplete will be raised to early.
1012+
currentSessions.cleanUp();
10081013
int currentSessionCount = getCurrentSessionCount();
10091014
if (currentSessionCount == 0) {
10101015
LOG.info("Firing node drain complete message");

java/src/org/openqa/selenium/remote/http/BUILD.bazel

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
load("@rules_jvm_external//:defs.bzl", "artifact")
21
load("//java:defs.bzl", "java_export")
32
load("//java:version.bzl", "SE_VERSION")
43

@@ -20,6 +19,5 @@ java_export(
2019
"//java:auto-service",
2120
"//java/src/org/openqa/selenium:core",
2221
"//java/src/org/openqa/selenium/json",
23-
artifact("dev.failsafe:failsafe"),
2422
],
2523
)

java/src/org/openqa/selenium/remote/http/RetryRequest.java

Lines changed: 45 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -17,89 +17,65 @@
1717

1818
package org.openqa.selenium.remote.http;
1919

20-
import static java.net.HttpURLConnection.HTTP_CLIENT_TIMEOUT;
2120
import static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR;
2221
import static java.net.HttpURLConnection.HTTP_UNAVAILABLE;
23-
import static org.openqa.selenium.internal.Debug.getDebugLogLevel;
24-
import static org.openqa.selenium.remote.http.Contents.asJson;
2522

26-
import dev.failsafe.Failsafe;
27-
import dev.failsafe.Fallback;
28-
import dev.failsafe.RetryPolicy;
29-
import dev.failsafe.event.ExecutionAttemptedEvent;
30-
import dev.failsafe.function.CheckedFunction;
3123
import java.net.ConnectException;
32-
import java.util.Map;
24+
import java.util.logging.Level;
3325
import java.util.logging.Logger;
26+
import org.openqa.selenium.internal.Debug;
3427

3528
public class RetryRequest implements Filter {
3629

3730
private static final Logger LOG = Logger.getLogger(RetryRequest.class.getName());
31+
private static final Level LOG_LEVEL = Debug.getDebugLogLevel();
3832

39-
private static final Fallback<HttpResponse> fallback =
40-
Fallback.of(
41-
(CheckedFunction<ExecutionAttemptedEvent<? extends HttpResponse>, ? extends HttpResponse>)
42-
RetryRequest::getFallback);
43-
44-
// Retry on connection error.
45-
private static final RetryPolicy<HttpResponse> connectionFailurePolicy =
46-
RetryPolicy.<HttpResponse>builder()
47-
.handleIf(failure -> failure.getCause() instanceof ConnectException)
48-
.withMaxRetries(3)
49-
.onRetry(
50-
e ->
51-
LOG.log(
52-
getDebugLogLevel(),
53-
"Connection failure #{0}. Retrying.",
54-
e.getAttemptCount()))
55-
.build();
56-
57-
// Retry if server is unavailable or an internal server error occurs without response body.
58-
private static final RetryPolicy<HttpResponse> serverErrorPolicy =
59-
RetryPolicy.<HttpResponse>builder()
60-
.handleResultIf(
61-
response ->
62-
response.getStatus() == HTTP_INTERNAL_ERROR
63-
&& Integer.parseInt((response).getHeader(HttpHeader.ContentLength.getName()))
64-
== 0)
65-
.handleResultIf(response -> (response).getStatus() == HTTP_UNAVAILABLE)
66-
.withMaxRetries(2)
67-
.onRetry(
68-
e ->
69-
LOG.log(
70-
getDebugLogLevel(),
71-
"Failure due to server error #{0}. Retrying.",
72-
e.getAttemptCount()))
73-
.build();
33+
private static final int RETRIES_ON_CONNECTION_FAILURE = 3;
34+
private static final int RETRIES_ON_SERVER_ERROR = 2;
35+
private static final int NEEDED_ATTEMPTS =
36+
Math.max(RETRIES_ON_CONNECTION_FAILURE, RETRIES_ON_SERVER_ERROR) + 1;
7437

7538
@Override
7639
public HttpHandler apply(HttpHandler next) {
77-
return req ->
78-
Failsafe.with(fallback)
79-
.compose(serverErrorPolicy)
80-
.compose(connectionFailurePolicy)
81-
.get(() -> next.execute(req));
82-
}
40+
return req -> {
41+
// start to preform the request in a loop, to allow retries
42+
for (int i = 0; i < NEEDED_ATTEMPTS; i++) {
43+
HttpResponse response;
44+
45+
try {
46+
response = next.execute(req);
47+
} catch (RuntimeException ex) {
48+
// detect a connection failure we would like to retry
49+
boolean isConnectionFailure = ex.getCause() instanceof ConnectException;
50+
51+
// must be a connection failure and check whether we have retries left for this
52+
if (isConnectionFailure && i < RETRIES_ON_CONNECTION_FAILURE) {
53+
LOG.log(LOG_LEVEL, "Retry #" + (i + 1) + " on ConnectException", ex);
54+
continue;
55+
}
8356

84-
private static HttpResponse getFallback(
85-
ExecutionAttemptedEvent<? extends HttpResponse> executionAttemptedEvent) throws Exception {
86-
if (executionAttemptedEvent.getLastException() != null) {
87-
Exception exception = (Exception) executionAttemptedEvent.getLastException();
88-
if (exception.getCause() instanceof ConnectException) {
89-
return new HttpResponse()
90-
.setStatus(HTTP_CLIENT_TIMEOUT)
91-
.setContent(asJson(Map.of("value", Map.of("message", "Connection failure"))));
92-
} else throw exception;
93-
} else if (executionAttemptedEvent.getLastResult() != null) {
94-
HttpResponse response = executionAttemptedEvent.getLastResult();
95-
if ((response.getStatus() == HTTP_INTERNAL_ERROR
96-
&& Integer.parseInt(response.getHeader(HttpHeader.ContentLength.getName())) == 0)
97-
|| response.getStatus() == HTTP_UNAVAILABLE) {
98-
return new HttpResponse()
99-
.setStatus(response.getStatus())
100-
.setContent(asJson(Map.of("value", Map.of("message", "Internal server error"))));
57+
// not a connection failure or retries exceeded, rethrow and let the caller handle this
58+
throw ex;
59+
}
60+
61+
// detect a server error we would like to retry
62+
boolean isServerError =
63+
(response.getStatus() == HTTP_INTERNAL_ERROR && response.getContent().length() == 0)
64+
|| response.getStatus() == HTTP_UNAVAILABLE;
65+
66+
// must be a server error and check whether we have retries left for this
67+
if (isServerError && i < RETRIES_ON_SERVER_ERROR) {
68+
LOG.log(LOG_LEVEL, "Retry #" + (i + 1) + " on ServerError: " + response.getStatus());
69+
continue;
70+
}
71+
72+
// not a server error or retries exceeded, return the result to the caller
73+
return response;
10174
}
102-
}
103-
return executionAttemptedEvent.getLastResult();
75+
76+
// This should not be reachable, we either retry or fail before. The only way to get here
77+
// is to set the static final int fields above to inconsistent values.
78+
throw new IllegalStateException("Effective unreachable code reached, check constants.");
79+
};
10480
}
10581
}

0 commit comments

Comments
 (0)