Skip to content

Commit f4537ef

Browse files
committed
no client creation after manager closing
1 parent 922327b commit f4537ef

File tree

2 files changed

+31
-5
lines changed

2 files changed

+31
-5
lines changed

modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3ClientsManager.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ public class S3ClientsManager implements ClusterStateApplier {
5353
private final Settings nodeS3Settings;
5454
private final Function<S3ClientSettings, AmazonS3Reference> clientBuilder;
5555
private final Executor executor;
56+
private final AtomicBoolean managerClosed = new AtomicBoolean(false);
5657
// A map of projectId to clients holder. Adding to and removing from the map happen only in the applier thread.
5758
private final Map<ProjectId, ClientsHolder<?>> clientsHolders;
5859

@@ -211,7 +212,12 @@ void releaseCachedClients(ProjectId projectId) {
211212
* Shutdown the manager by closing all clients holders. This is called when the node is shutting down.
212213
*/
213214
void close() {
214-
IOUtils.closeWhileHandlingException(clientsHolders.values());
215+
if (managerClosed.compareAndSet(false, true)) {
216+
// Close all clients holders, they will close their cached clients.
217+
// It's OK if a new clients holder is added concurrently or after this point because
218+
// no new client will be created once the manager is closed, i.e. nothing to release.
219+
IOUtils.closeWhileHandlingException(clientsHolders.values());
220+
}
215221
}
216222

217223
private boolean newOrUpdated(ProjectId projectId, Map<String, S3ClientSettings> currentClientSettings) {
@@ -292,6 +298,11 @@ final AmazonS3Reference client(RepositoryMetadata repositoryMetadata) {
292298
// Not adding a new client once the manager is closed since there won't be anything to close it
293299
throw new IllegalStateException("Project [" + projectId() + "] clients holder is closed");
294300
}
301+
if (managerClosed.get()) {
302+
// This clients holder must be added after the manager is closed. It must have no cached clients.
303+
assert clientsCache.isEmpty() : "expect empty cache, but got " + clientsCache;
304+
throw new IllegalStateException("s3 clients manager is closed");
305+
}
295306
// The close() method maybe called after we checked it, it is ok since we are already inside the synchronized block.
296307
// The clearCache() will clear the newly added client.
297308
final var newClientReference = clientBuilder.apply(settings);

modules/repository-s3/src/test/java/org/elasticsearch/repositories/s3/S3ClientsManagerTests.java

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,8 @@
4242
import java.util.HashMap;
4343
import java.util.List;
4444
import java.util.Map;
45-
import java.util.concurrent.CountDownLatch;
4645
import java.util.concurrent.ExecutionException;
4746
import java.util.concurrent.atomic.AtomicInteger;
48-
import java.util.concurrent.atomic.AtomicReference;
4947
import java.util.stream.Collectors;
5048
import java.util.stream.IntStream;
5149

@@ -72,13 +70,11 @@ public class S3ClientsManagerTests extends ESTestCase {
7270
private ClusterService clusterService;
7371
private S3Service s3Service;
7472
private S3ClientsManager s3ClientsManager;
75-
private final AtomicReference<CountDownLatch> clientRefsCloseLatchRef = new AtomicReference<>();
7673

7774
@Override
7875
public void setUp() throws Exception {
7976
super.setUp();
8077
s3SecretsIdGenerators = ConcurrentCollections.newConcurrentMap();
81-
clientRefsCloseLatchRef.set(null);
8278
clientNames = IntStream.range(0, between(2, 5)).mapToObj(i -> randomIdentifier() + "_" + i).toList();
8379

8480
final Settings.Builder builder = Settings.builder();
@@ -296,6 +292,25 @@ public void testClusterAndProjectClients() {
296292
assertFalse(projectClient.hasReferences());
297293
}
298294

295+
public void testClientsHolderAfterManagerClosed() {
296+
final ProjectId projectId = randomUniqueProjectId();
297+
final String clientName = randomFrom(clientNames);
298+
299+
s3ClientsManager.close();
300+
// New holder can be added after the manager is closed, but no actual client can be created
301+
updateProjectInClusterState(projectId, newProjectClientsSecrets(projectId, clientName));
302+
try (var clientsHolder = s3ClientsManager.getClientsHolders().get(projectId)) {
303+
assertNotNull(clientsHolder);
304+
assertFalse(clientsHolder.isClosed());
305+
306+
final IllegalStateException e = expectThrows(
307+
IllegalStateException.class,
308+
() -> s3ClientsManager.client(projectId, createRepositoryMetadata(clientName))
309+
);
310+
assertThat(e.getMessage(), containsString("s3 clients manager is closed"));
311+
}
312+
}
313+
299314
public void testProjectClientsDisabled() {
300315
final var clusterService = spy(this.clusterService);
301316
final S3Service s3ServiceWithNoProjectSupport = new S3Service(

0 commit comments

Comments
 (0)