Skip to content

Commit cba172b

Browse files
committed
separate cluster and project holders
1 parent ea91764 commit cba172b

File tree

2 files changed

+57
-34
lines changed

2 files changed

+57
-34
lines changed

modules/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageClientsManager.java

Lines changed: 56 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@
1818
import org.elasticsearch.common.settings.ProjectSecrets;
1919
import org.elasticsearch.common.settings.Settings;
2020
import org.elasticsearch.common.util.Maps;
21+
import org.elasticsearch.common.util.concurrent.ConcurrentCollections;
2122
import org.elasticsearch.logging.LogManager;
2223
import org.elasticsearch.logging.Logger;
2324

2425
import java.io.IOException;
2526
import java.util.HashMap;
2627
import java.util.Map;
27-
import java.util.concurrent.ConcurrentHashMap;
2828
import java.util.stream.Collectors;
2929

3030
import static java.util.Collections.emptyMap;
@@ -41,7 +41,8 @@ public class GoogleCloudStorageClientsManager implements ClusterStateApplier {
4141
GcsRepositoryStatsCollector,
4242
MeteredStorage,
4343
IOException> clientBuilder;
44-
private final Map<ProjectId, ClientsHolder> clientHolders;
44+
private final ClusterClientsHolder clusterClientsHolder = new ClusterClientsHolder();
45+
private final Map<ProjectId, ClientsHolder> projectClientHolders;
4546

4647
public GoogleCloudStorageClientsManager(
4748
Settings nodeSettings,
@@ -54,16 +55,15 @@ public GoogleCloudStorageClientsManager(
5455
.build();
5556
this.clientBuilder = clientBuilder;
5657
if (supportsMultipleProjects) {
57-
// If multiple projects are supported, we need to track per-project clients
58-
this.clientHolders = new ConcurrentHashMap<>(Map.of(ProjectId.DEFAULT, new ClientsHolder()));
58+
this.projectClientHolders = ConcurrentCollections.newConcurrentMap();
5959
} else {
60-
// If only a single project is supported, we use a single holder for the default project
61-
this.clientHolders = Map.of(ProjectId.DEFAULT, new ClientsHolder());
60+
this.projectClientHolders = null;
6261
}
6362
}
6463

6564
@Override
6665
public void applyClusterState(ClusterChangedEvent event) {
66+
assert projectClientHolders != null;
6767
final Map<ProjectId, ProjectMetadata> currentProjects = event.state().metadata().projects();
6868

6969
final var updatedPerProjectClients = new HashMap<ProjectId, ClientsHolder>();
@@ -77,7 +77,7 @@ public void applyClusterState(ClusterChangedEvent event) {
7777
// Project secrets can be null when node restarts. It may not have any s3 credentials if s3 is not in use.
7878
if (projectSecrets == null || projectSecrets.getSettingNames().stream().noneMatch(key -> key.startsWith(GCS_SETTING_PREFIX))) {
7979
// Most likely there won't be any existing client, but attempt to remove it anyway just in case
80-
clientHolders.remove(project.id());
80+
projectClientHolders.remove(project.id());
8181
continue;
8282
}
8383

@@ -106,20 +106,20 @@ public void applyClusterState(ClusterChangedEvent event) {
106106
// TODO: If performance is an issue, we may consider comparing just the relevant project secrets for new or updated clients
107107
// and avoid building the clientSettings
108108
if (newOrUpdated(project.id(), clientSettings)) {
109-
updatedPerProjectClients.put(project.id(), new ClientsHolder());
109+
updatedPerProjectClients.put(project.id(), new PerProjectClientsHolder(clientSettings));
110110
}
111111
}
112112

113113
// Updated projects
114114
for (var projectId : updatedPerProjectClients.keySet()) {
115115
assert ProjectId.DEFAULT.equals(projectId) == false;
116-
clientHolders.put(projectId, updatedPerProjectClients.get(projectId));
116+
projectClientHolders.put(projectId, updatedPerProjectClients.get(projectId));
117117
}
118118
// Removed projects
119-
for (var projectId : clientHolders.keySet()) {
119+
for (var projectId : projectClientHolders.keySet()) {
120120
if (currentProjects.containsKey(projectId) == false) {
121121
assert ProjectId.DEFAULT.equals(projectId) == false;
122-
clientHolders.remove(projectId);
122+
projectClientHolders.remove(projectId);
123123
}
124124
}
125125
}
@@ -129,54 +129,53 @@ MeteredStorage client(String clientName, String repositoryName, GcsRepositorySta
129129
return client(ProjectId.DEFAULT, clientName, repositoryName, statsCollector);
130130
}
131131

132-
@Deprecated(forRemoval = true)
133-
void refreshAndClearCache(Map<String, GoogleCloudStorageClientSettings> clientsSettings) {
134-
refreshAndClearCache(ProjectId.DEFAULT, clientsSettings);
135-
}
136-
137132
@Deprecated(forRemoval = true)
138133
void closeRepositoryClients(String repositoryName) {
139134
closeRepositoryClients(ProjectId.DEFAULT, repositoryName);
140135
}
141136

137+
void refreshAndClearCacheForClusterClients(Map<String, GoogleCloudStorageClientSettings> clientsSettings) {
138+
clusterClientsHolder.refreshAndClearCache(clientsSettings);
139+
}
140+
142141
MeteredStorage client(ProjectId projectId, String clientName, String repositoryName, GcsRepositoryStatsCollector statsCollector)
143142
throws IOException {
144143
return getClientsHolderSafe(projectId).client(clientName, repositoryName, statsCollector);
145144
}
146145

147-
void refreshAndClearCache(ProjectId projectId, Map<String, GoogleCloudStorageClientSettings> clientsSettings) {
148-
getClientsHolderSafe(projectId).refreshAndClearCache(clientsSettings);
149-
}
150-
151146
void closeRepositoryClients(ProjectId projectId, String repositoryName) {
152147
getClientsHolderSafe(projectId).closeRepositoryClients(repositoryName);
153148
}
154149

155150
private boolean newOrUpdated(ProjectId projectId, Map<String, GoogleCloudStorageClientSettings> currentClientSettings) {
156-
final var old = clientHolders.get(projectId);
151+
final var old = projectClientHolders.get(projectId);
157152
if (old == null) {
158153
return true;
159154
}
160-
return currentClientSettings.equals(old.clientSettings) == false;
155+
return currentClientSettings.equals(old.getClientSettings()) == false;
161156
}
162157

163158
private ClientsHolder getClientsHolderSafe(ProjectId projectId) {
164-
final var clientsHolder = clientHolders.get(projectId);
159+
final var clientsHolder = projectClientHolders.get(projectId);
165160
if (clientsHolder == null) {
166161
assert ProjectId.DEFAULT.equals(projectId) == false;
167162
throw new IllegalArgumentException("No GCS client is configured for project [" + projectId + "]");
168163
}
169164
return clientsHolder;
170165
}
171166

172-
class ClientsHolder {
167+
abstract class ClientsHolder {
173168

174-
private volatile Map<String, GoogleCloudStorageClientSettings> clientSettings = emptyMap();
175169
/**
176170
* Dictionary of client instances. Client instances are built lazily from the
177171
* latest settings. Clients are cached by a composite repositoryName key.
178172
*/
179-
private volatile Map<String, MeteredStorage> clientCache = emptyMap();
173+
protected volatile Map<String, MeteredStorage> clientCache = emptyMap();
174+
175+
/**
176+
* Get the current client settings for all clients in this holder.
177+
*/
178+
protected abstract Map<String, GoogleCloudStorageClientSettings> getClientSettings();
180179

181180
/**
182181
* Attempts to retrieve a client from the cache. If the client does not exist it
@@ -205,14 +204,14 @@ MeteredStorage client(final String clientName, final String repositoryName, fina
205204
return existing;
206205
}
207206

208-
final GoogleCloudStorageClientSettings settings = clientSettings.get(clientName);
207+
final GoogleCloudStorageClientSettings settings = getClientSettings().get(clientName);
209208

210209
if (settings == null) {
211210
throw new IllegalArgumentException(
212211
"Unknown client name ["
213212
+ clientName
214213
+ "]. Existing client configs: "
215-
+ Strings.collectionToDelimitedString(clientSettings.keySet(), ",")
214+
+ Strings.collectionToDelimitedString(getClientSettings().keySet(), ",")
216215
);
217216
}
218217

@@ -223,6 +222,23 @@ MeteredStorage client(final String clientName, final String repositoryName, fina
223222
}
224223
}
225224

225+
synchronized void closeRepositoryClients(String repositoryName) {
226+
clientCache = clientCache.entrySet()
227+
.stream()
228+
.filter(entry -> entry.getKey().equals(repositoryName) == false)
229+
.collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, java.util.Map.Entry::getValue));
230+
}
231+
}
232+
233+
final class ClusterClientsHolder extends ClientsHolder {
234+
235+
private volatile Map<String, GoogleCloudStorageClientSettings> clientSettings = emptyMap();
236+
237+
@Override
238+
protected Map<String, GoogleCloudStorageClientSettings> getClientSettings() {
239+
return clientSettings;
240+
}
241+
226242
/**
227243
* Refreshes the client settings and clears the client cache. Subsequent calls to
228244
* {@code GoogleCloudStorageService#client} will return new clients constructed
@@ -234,12 +250,19 @@ synchronized void refreshAndClearCache(Map<String, GoogleCloudStorageClientSetti
234250
this.clientCache = emptyMap();
235251
this.clientSettings = Maps.ofEntries(clientsSettings.entrySet());
236252
}
253+
}
237254

238-
synchronized void closeRepositoryClients(String repositoryName) {
239-
clientCache = clientCache.entrySet()
240-
.stream()
241-
.filter(entry -> entry.getKey().equals(repositoryName) == false)
242-
.collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue));
255+
final class PerProjectClientsHolder extends ClientsHolder {
256+
257+
private final Map<String, GoogleCloudStorageClientSettings> clientSettings;
258+
259+
PerProjectClientsHolder(Map<String, GoogleCloudStorageClientSettings> clientSettings) {
260+
this.clientSettings = clientSettings;
261+
}
262+
263+
@Override
264+
protected Map<String, GoogleCloudStorageClientSettings> getClientSettings() {
265+
return clientSettings;
243266
}
244267
}
245268
}

modules/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ public boolean isServerless() {
8282
* @param clientsSettings the new settings used for building clients for subsequent requests
8383
*/
8484
public void refreshAndClearCache(Map<String, GoogleCloudStorageClientSettings> clientsSettings) {
85-
clientsManager.refreshAndClearCache(clientsSettings);
85+
clientsManager.refreshAndClearCacheForClusterClients(clientsSettings);
8686
}
8787

8888
/**

0 commit comments

Comments
 (0)