Skip to content

Commit a3df73c

Browse files
krmahadevandiemol
andauthored
[grid] Streamline cleaning up of download/upload folders (#12059)
* [grid] Streamline cleaning up of download/upload folders The download/upload folders should not be evicted from cache via a timer. They should instead be explicitly invalidated only when a session gets either evicted or invalidated. The sesssion removal should cascade cleaning up of the caches for download and upload folders as well. * Formatting files --------- Co-authored-by: Diego Molina <[email protected]> Co-authored-by: Diego Molina <[email protected]>
1 parent fb986f8 commit a3df73c

2 files changed

Lines changed: 83 additions & 23 deletions

File tree

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

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -182,42 +182,59 @@ protected LocalNode(
182182
}
183183
: healthCheck;
184184

185+
// Do not clear this cache automatically using a timer.
186+
// It will be explicitly cleaned up, as and when "sessionToDownloadsDir" is auto cleaned.
185187
this.uploadsTempFileSystem =
186188
CacheBuilder.newBuilder()
187-
.expireAfterAccess(sessionTimeout)
188-
.ticker(ticker)
189189
.removalListener(
190190
(RemovalListener<SessionId, TemporaryFilesystem>)
191-
notification -> {
192-
TemporaryFilesystem tempFS = notification.getValue();
193-
tempFS.deleteTemporaryFiles();
194-
tempFS.deleteBaseDir();
195-
})
191+
notification ->
192+
Optional.ofNullable(notification.getValue())
193+
.ifPresent(
194+
tempFS -> {
195+
tempFS.deleteTemporaryFiles();
196+
tempFS.deleteBaseDir();
197+
}))
196198
.build();
197199

200+
// Do not clear this cache automatically using a timer.
201+
// It will be explicitly cleaned up, as and when "sessionToDownloadsDir" is auto cleaned.
198202
this.downloadsTempFileSystem =
199203
CacheBuilder.newBuilder()
200-
.expireAfterAccess(sessionTimeout)
201-
.ticker(ticker)
202204
.removalListener(
203205
(RemovalListener<UUID, TemporaryFilesystem>)
204-
notification -> {
205-
TemporaryFilesystem tempFS = notification.getValue();
206-
tempFS.deleteTemporaryFiles();
207-
tempFS.deleteBaseDir();
208-
})
206+
notification ->
207+
Optional.ofNullable(notification.getValue())
208+
.ifPresent(
209+
fs -> {
210+
fs.deleteTemporaryFiles();
211+
fs.deleteBaseDir();
212+
}))
209213
.build();
210214

215+
// Do not clear this cache automatically using a timer.
216+
// It will be explicitly cleaned up, as and when "currentSessions" is auto cleaned.
211217
this.sessionToDownloadsDir =
212218
CacheBuilder.newBuilder()
213-
.expireAfterAccess(sessionTimeout)
214-
.ticker(ticker)
215219
.removalListener(
216220
(RemovalListener<SessionId, UUID>)
217221
notification -> {
218-
if (notification.getValue() != null) {
219-
this.downloadsTempFileSystem.invalidate(notification.getValue());
220-
}
222+
Optional.ofNullable(notification.getValue())
223+
.ifPresent(
224+
value -> {
225+
downloadsTempFileSystem.invalidate(value);
226+
LOG.warning(
227+
"Removing Downloads folder associated with "
228+
+ notification.getKey());
229+
});
230+
Optional.ofNullable(notification.getKey())
231+
.ifPresent(
232+
value -> {
233+
uploadsTempFileSystem.invalidate(value);
234+
LOG.warning(
235+
"Removing Uploads folder associated with "
236+
+ notification.getKey());
237+
});
221238
})
222239
.build();
223240

@@ -294,9 +311,6 @@ private void stopTimedOutSession(RemovalNotification<SessionId, SessionSlot> not
294311
}
295312
// Attempt to stop the session
296313
slot.stop();
297-
// Invalidate uploads temp file system
298-
this.uploadsTempFileSystem.invalidate(id);
299-
// Invalidate downloads temp file system
300314
this.sessionToDownloadsDir.invalidate(id);
301315
// Decrement pending sessions if Node is draining
302316
if (this.isDraining()) {

java/test/org/openqa/selenium/grid/node/NodeTest.java

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,11 @@
4949
import java.util.Optional;
5050
import java.util.UUID;
5151
import java.util.concurrent.CountDownLatch;
52+
import java.util.concurrent.TimeUnit;
5253
import java.util.concurrent.atomic.AtomicBoolean;
5354
import java.util.concurrent.atomic.AtomicReference;
55+
import java.util.function.Consumer;
56+
import java.util.function.Function;
5457
import org.junit.jupiter.api.BeforeEach;
5558
import org.junit.jupiter.api.DisplayName;
5659
import org.junit.jupiter.api.Test;
@@ -144,7 +147,7 @@ public HttpResponse execute(HttpRequest req) throws UncheckedIOException {
144147
.add(caps, new TestSessionFactory((id, c) -> new Handler(c)))
145148
.maximumConcurrentSessions(2);
146149
if (isDownloadsTestCase) {
147-
builder = builder.enableManagedDownloads(true);
150+
builder = builder.enableManagedDownloads(true).sessionTimeout(Duration.ofSeconds(1));
148151
}
149152
local = builder.build();
150153

@@ -636,6 +639,49 @@ void canListFilesToDownload() throws IOException {
636639
}
637640
}
638641

642+
@Test
643+
@DisplayName("DownloadsTestCase")
644+
void ensureImmunityToSessionTimeOutsForFileDownloads() throws InterruptedException {
645+
Consumer<HttpResponse> DOWNLOAD_RSP_VALIDATOR =
646+
response -> {
647+
Map<String, Object> map = new Json().toType(string(response), Json.MAP_TYPE);
648+
assertThat(map).isNotNull();
649+
List<String> files =
650+
Optional.ofNullable(map.get("value"))
651+
.map(data -> (Map<String, Object>) data)
652+
.map(data -> (List<String>) data.get("names"))
653+
.orElseThrow(() -> new IllegalStateException("Could not find value attribute"));
654+
assertThat(files).isEmpty();
655+
};
656+
657+
Function<Session, HttpResponse> TRIGGER_LIST_FILES =
658+
session -> {
659+
HttpRequest req =
660+
new HttpRequest(GET, String.format("/session/%s/se/files", session.getId()));
661+
HttpResponse response = node.execute(req);
662+
assertThat(response.getStatus()).isEqualTo(200);
663+
return response;
664+
};
665+
666+
Either<WebDriverException, CreateSessionResponse> response =
667+
node.newSession(createSessionRequest(caps));
668+
assertThatEither(response).isRight();
669+
Session session = response.right().getSession();
670+
try {
671+
HttpResponse rsp = TRIGGER_LIST_FILES.apply(session);
672+
// Totally we will try list files 3 times, each with a gap of 900 ms.
673+
// The session timeout is defined as 1 second. So we try and ensure that
674+
// even after trying to download 3 times, we don't hit any errors.
675+
for (int i = 0; i < 3; i++) {
676+
DOWNLOAD_RSP_VALIDATOR.accept(rsp);
677+
node.isSessionOwner(session.getId()); // Keep session active so that we dont timeout
678+
TimeUnit.MILLISECONDS.sleep(700);
679+
}
680+
} finally {
681+
node.stop(session.getId());
682+
}
683+
}
684+
639685
@Test
640686
void canDownloadFileThrowsErrorMsgWhenDownloadsDirNotSpecified() {
641687
Either<WebDriverException, CreateSessionResponse> response =

0 commit comments

Comments
 (0)