Skip to content
This repository was archived by the owner on Apr 28, 2025. It is now read-only.

Commit 0997128

Browse files
committed
Significantly improved logic for restarting grid nodes, to prevent orphan session in the new session queue and chances of a bad TestSlot that cannot be cleaned up.
1 parent ab636be commit 0997128

File tree

12 files changed

+128
-70
lines changed

12 files changed

+128
-70
lines changed

CHANGELOG.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
1.6.1 - [BUG] Significantly improved logic for restarting grid nodes, to prevent orphan session in the new session queue and chances of a bad TestSlot that cannot be cleaned up.
12
1.6.0 - Better session tracking, on the /session_history endpoint
23
- [BUG] fixing inability to start the grid hub because the webdriver 2.44 has broken JSON dependency
34
- Adding a machine view at http://localhost:3000

SeleniumGridExtras/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>com.groupon.selenium-grid-extras</groupId>
88
<artifactId>SeleniumGridExtras</artifactId>
9-
<version>1.6.0-SNAPSHOT</version>
9+
<version>1.6.1-SNAPSHOT</version>
1010

1111
<repositories>
1212
<repository>

SeleniumGridExtras/src/main/java/com/groupon/seleniumgridextras/config/FirstTimeRunConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ private static void askToRecordVideo(Config defaultConfig) {
107107
logger.info("Setting node to record videos");
108108

109109
answer =
110-
askQuestion("How many videos should node keep in history?",
110+
askQuestion("How many videos should node keep in threads?",
111111
"10");
112112

113113
logger.info("Will keep " + answer + " videos");

SeleniumGridExtras/src/main/java/com/groupon/seleniumgridextras/grid/proxies/SetupTeardownProxy.java

Lines changed: 20 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
import com.google.gson.JsonObject;
4242
import com.google.gson.JsonParser;
4343

44-
import com.groupon.seleniumgridextras.grid.proxies.sessions.history.SessionHistoryThreadPool;
44+
import com.groupon.seleniumgridextras.grid.proxies.sessions.threads.NodeRestartCallable;
4545
import com.groupon.seleniumgridextras.utilities.HttpUtility;
4646
import com.groupon.seleniumgridextras.utilities.JsonWireCommandTranslator;
4747

@@ -65,6 +65,8 @@
6565
import java.util.LinkedList;
6666
import java.util.List;
6767
import java.util.Map;
68+
import java.util.concurrent.ExecutorService;
69+
import java.util.concurrent.Executors;
6870
import java.util.concurrent.Future;
6971

7072
import javax.servlet.http.HttpServletRequest;
@@ -76,6 +78,7 @@ public class SetupTeardownProxy extends DefaultRemoteProxy implements TestSessio
7678
private boolean available = true;
7779
private boolean restarting = false;
7880
private List<String> sessionsRecording = new LinkedList<String>();
81+
protected static ExecutorService cachedPool;
7982

8083
private static Logger logger = Logger.getLogger(SetupTeardownProxy.class);
8184

@@ -85,31 +88,6 @@ public SetupTeardownProxy(RegistrationRequest request, Registry registry) {
8588
writeProxyLog("Attaching a node: " + getHost());
8689
}
8790

88-
@Override
89-
public TestSession getNewSession(Map<String, Object> requestedCapability) {
90-
synchronized (this) {
91-
if (timeToReboot() && !this.isBusy()) {
92-
setAvailable(false);
93-
setRestarting(false);
94-
// killBrowserForCurrentSession();
95-
stopGridNode();
96-
rebootGridExtrasNode();
97-
}
98-
99-
if (isAvailable()) {
100-
TestSession session = super.getNewSession(requestedCapability);
101-
if (session == null) {
102-
return null;
103-
} else {
104-
SessionHistoryThreadPool.registerNewSession(session);
105-
return session;
106-
}
107-
} else {
108-
return null;
109-
}
110-
111-
}
112-
}
11391

11492
@Override
11593
public void beforeCommand(TestSession session, HttpServletRequest request,
@@ -137,6 +115,12 @@ public void afterSession(TestSession session) {
137115
super.afterSession(session);
138116
stopVideoRecording(session.getExternalKey().getKey());
139117
callRemoteGridExtrasAsync("teardown", new HashMap<String, String>());
118+
119+
if (cachedPool == null) {
120+
cachedPool = Executors.newCachedThreadPool();
121+
}
122+
123+
cachedPool.submit(new NodeRestartCallable(this, session));
140124
}
141125

142126
private boolean alreadyRecordingCurrentSession(String session) {
@@ -183,13 +167,13 @@ private void updateLastCommand(String session, HttpServletRequest request) {
183167
callRemoteGridExtrasAsync("video", params);
184168
}
185169

186-
protected void stopGridNode() {
170+
public void stopGridNode() {
187171
writeProxyLog("Asking " + getHost() + " to stop grid node politely");
188172
writeProxyLog(callRemoteGridExtras("stop_grid?port=5555"));
189173
unregister();
190174
}
191175

192-
private void rebootGridExtrasNode() {
176+
public void rebootGridExtrasNode() {
193177
writeProxyLog("Asking SeleniumGridExtras to reboot " + getHost());
194178
writeProxyLog(callRemoteGridExtras("reboot"));
195179
}
@@ -224,7 +208,8 @@ private Future<String> callRemoteGridExtrasAsync(String action, Map<String, Stri
224208
return returnedFuture;
225209
}
226210

227-
private JsonObject callRemoteGridExtras(String action) {
211+
212+
public JsonObject callRemoteGridExtras(String action) {
228213
String returnedString;
229214

230215
try {
@@ -237,13 +222,13 @@ private JsonObject callRemoteGridExtras(String action) {
237222
return (JsonObject) j.parse(returnedString);
238223

239224
} catch (MalformedURLException e) {
240-
writeProxyLog(e.toString());
241-
e.printStackTrace();
225+
logger.error("Attempt to contact node has failed with " + e.getMessage(), e);
242226
} catch (ProtocolException e) {
243-
writeProxyLog(e.toString());
244-
e.printStackTrace();
227+
logger.error("Attempt to contact node has failed with " + e.getMessage(), e);
245228
} catch (IOException e) {
246-
e.printStackTrace();
229+
logger.error("Attempt to contact node has failed with " + e.getMessage(), e);
230+
} catch (Exception e) {
231+
logger.error("Attempt to contact node has failed with " + e.getMessage(), e);
247232
}
248233

249234
return null;
@@ -257,35 +242,11 @@ protected boolean isAvailable() {
257242
return this.available;
258243
}
259244

260-
protected void setAvailable(boolean available) {
245+
public void setAvailable(boolean available) {
261246
this.available = available;
262247
}
263248

264-
protected boolean timeToReboot() {
265-
JsonObject status = callRemoteGridExtras("grid_status");
266-
267-
boolean nodeRunning = status.get("node_running").getAsBoolean();
268-
int sessionsStarted = status.get("node_sessions_started").getAsInt();
269-
int sessionLimit = status.get("node_sessions_limit").getAsInt();
270-
271-
if (!nodeRunning) {
272-
writeProxyLog("The grid node on " + getHost() + " does not seem to be running");
273-
} else if (sessionLimit == 0) {
274-
return false;
275-
} else if (sessionsStarted >= sessionLimit) {
276-
System.out
277-
.println("Node " + getHost() + " has reached " + sessionsStarted + " of " + sessionLimit
278-
+ " test session, time to reboot");
279-
} else {
280-
return false;
281-
282-
}
283-
setAvailable(false);
284-
return true;
285-
}
286-
287249
public void unregister() {
288-
writeProxyLog("Sending Un register command for " + getHost());
289250
addNewEvent(new RemoteUnregisterException("Unregistering the node."));
290251
}
291252

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package com.groupon.seleniumgridextras.grid.proxies.sessions.threads;
2+
3+
import com.google.gson.JsonObject;
4+
import com.groupon.seleniumgridextras.grid.proxies.SetupTeardownProxy;
5+
import org.apache.log4j.Logger;
6+
import org.openqa.grid.internal.TestSession;
7+
import org.openqa.grid.selenium.proxy.DefaultRemoteProxy;
8+
9+
import java.util.concurrent.Callable;
10+
11+
12+
public class NodeRestartCallable implements Callable {
13+
14+
public static final int TIME_FOR_PROXY_TO_FREEUP = 2000;
15+
private static Logger logger = Logger.getLogger(NodeRestartCallable.class);
16+
17+
protected SetupTeardownProxy proxy;
18+
protected TestSession session;
19+
20+
public NodeRestartCallable(SetupTeardownProxy proxy, TestSession session) {
21+
this.proxy = proxy;
22+
this.session = session;
23+
}
24+
25+
26+
@Override
27+
public String call() throws Exception {
28+
//TODO: This is a huge improvment in the reboot logic compared to how it used to be. But this method needs clean up done by fresh eyes
29+
30+
try {
31+
//Giving the proxy a couple of seconds to recover post session. This also gives us opportunity to check if the new build is trying to pick it up
32+
logger.info(String.format("Giving %s proxy %s ms to free up", this.proxy.getId(), TIME_FOR_PROXY_TO_FREEUP));
33+
Thread.sleep(TIME_FOR_PROXY_TO_FREEUP);
34+
} catch (InterruptedException e) {
35+
logger.error(e);
36+
}
37+
38+
try {
39+
40+
41+
42+
if (this.proxy.isBusy() || this.proxy.getRegistry().getNewSessionRequestCount() != 0){
43+
String message = String.format("Proxy %s is currently %s busy and has there are %s items in the queue. Will not attempt to reboot node until the grid is free",
44+
this.proxy.getId(),
45+
(this.proxy.isBusy() ? "" : "not"),
46+
this.proxy.getRegistry().getNewSessionRequestCount());
47+
48+
logger.info(message);
49+
return message;
50+
}
51+
52+
JsonObject status = proxy.callRemoteGridExtras("grid_status");
53+
54+
if (status == null) {
55+
String message = String.format("Problem communicating with %s, will not attempt to reboot", proxy.getRemoteHost().getHost());
56+
logger.warn(message);
57+
return message;
58+
}
59+
60+
int sessionsStarted = status.get("node_sessions_started").getAsInt();
61+
int sessionLimit = status.get("node_sessions_limit").getAsInt();
62+
63+
if (sessionLimit == 0) {
64+
String message = String.format("Node %s with proxy %s is set to never reboot, skipping this step",
65+
proxy.getRemoteHost().getHost(), proxy.getId());
66+
logger.info(message);
67+
return message;
68+
}
69+
70+
if (sessionsStarted >= sessionLimit) {
71+
String message = String.format("Node %s has executed has executed %s sessions, the limit is %s so it is time to reboot it",
72+
proxy.getRemoteHost().getHost(), sessionsStarted, sessionLimit);
73+
74+
logger.info(message);
75+
proxy.setAvailable(false);
76+
proxy.setRestarting(false);
77+
proxy.stopGridNode();
78+
proxy.rebootGridExtrasNode();
79+
80+
81+
return message;
82+
}
83+
} catch (Exception e) {
84+
//Capture any unexpected exception and print it to log. Currently the hub will completely ignore any
85+
//Exception, this way at least we can try to debug it.
86+
String message = String.format("Something didn't go right when trying to reboot node %s : %s",
87+
proxy.getRemoteHost().getHost(), e.getMessage());
88+
logger.error(message, e);
89+
return message;
90+
}
91+
92+
return ""; //Should never get to this point
93+
}
94+
95+
96+
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.groupon.seleniumgridextras.grid.proxies.sessions.history;
1+
package com.groupon.seleniumgridextras.grid.proxies.sessions.threads;
22

33
import com.groupon.seleniumgridextras.config.DefaultConfig;
44
import com.groupon.seleniumgridextras.loggers.SessionHistoryLog;
@@ -58,7 +58,7 @@ public String call() throws Exception {
5858
SessionHistoryLog.setOutputDir(DefaultConfig.SESSION_LOG_DIRECTORY);
5959
SessionHistoryLog.newSession(this.session.getSlot().getRemoteURL().getHost(), sessionDetails);
6060
} catch (Exception e) {
61-
logger.error("Something went wrong when gathering information for new session history", e);
61+
logger.error("Something went wrong when gathering information for new session threads", e);
6262
}
6363
return sessionDetails.toString();
6464
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.groupon.seleniumgridextras.grid.proxies.sessions.history;
1+
package com.groupon.seleniumgridextras.grid.proxies.sessions.threads;
22

33
import org.apache.log4j.Logger;
44
import org.openqa.grid.internal.TestSession;

SeleniumGridExtras/src/main/java/com/groupon/seleniumgridextras/loggers/NodeSessionHistory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public void backupToFile(){
4646
try {
4747
FileIOUtility.writePrettyJsonToFile(outputFile, sessions, false);
4848
} catch (IOException e) {
49-
logger.warn("Unable to backup session history to file for " + outputFile.getAbsolutePath());
49+
logger.warn("Unable to backup session threads to file for " + outputFile.getAbsolutePath());
5050
logger.warn(e);
5151
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
5252
}

SeleniumGridExtras/src/main/java/com/groupon/seleniumgridextras/loggers/SessionHistoryLog.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ protected static void resetMemory() {
5353
history = new HashMap<String, NodeSessionHistory>();
5454
} else {
5555
//delete all reference to objects to make objects ready for garbage collection
56-
//Doing it this way, just in case the history map is refered to anywhere, and does not get garbage collected
56+
//Doing it this way, just in case the threads map is refered to anywhere, and does not get garbage collected
5757
//This is done in hopes of reducing memory bloat, if a JVM expert is reading this, please fix it!
5858
//-Love, Dima
5959
history.clear();

SeleniumGridExtras/src/main/java/com/groupon/seleniumgridextras/tasks/SessionHistory.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public SessionHistory() {
2121
// params.addProperty(JsonCodec.WebDriver.Grid.HOST, "Host on which the session is kept");
2222

2323

24-
addResponseDescription(JsonCodec.WebDriver.Grid.LOGS, "An array with session history per node");
24+
addResponseDescription(JsonCodec.WebDriver.Grid.LOGS, "An array with session threads per node");
2525

2626

2727
setAcceptedParams(params);
@@ -39,7 +39,7 @@ public JsonObject execute() {
3939
try {
4040
getJsonResponse().addNestedMapValues(JsonCodec.WebDriver.Grid.LOGS, SessionHistoryLog.getTodaysHistoryAsMap());
4141
} catch (Exception e) {
42-
String error = String.format("Something went wrong when reading session history\n%s",
42+
String error = String.format("Something went wrong when reading session threads\n%s",
4343
Throwables.getStackTraceAsString(e));
4444
logger.error(error);
4545
getJsonResponse().addKeyValues(JsonCodec.ERROR, error);

0 commit comments

Comments
 (0)