Skip to content

Commit 49c5640

Browse files
committed
Cache clients by purpose, add purpose to HttpStatsCollector
1 parent a0cf1ba commit 49c5640

File tree

3 files changed

+39
-17
lines changed

3 files changed

+39
-17
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ class GoogleCloudStorageBlobStore implements BlobStore {
130130
}
131131

132132
private Storage client(OperationPurpose purpose) throws IOException {
133-
return storageService.client(clientName, repositoryName, stats);
133+
return storageService.client(clientName, repositoryName, purpose, stats);
134134
}
135135

136136
@Override
@@ -140,7 +140,7 @@ public BlobContainer blobContainer(BlobPath path) {
140140

141141
@Override
142142
public void close() {
143-
storageService.closeRepositoryClient(repositoryName);
143+
storageService.closeRepositoryClients(repositoryName);
144144
}
145145

146146
/**

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
import com.google.api.client.http.HttpResponse;
1515
import com.google.api.client.http.HttpResponseInterceptor;
1616

17+
import org.elasticsearch.common.blobstore.OperationPurpose;
18+
1719
import java.util.List;
1820
import java.util.Locale;
1921
import java.util.function.Consumer;
@@ -43,10 +45,12 @@ final class GoogleCloudStorageHttpStatsCollector implements HttpResponseIntercep
4345
);
4446

4547
private final GoogleCloudStorageOperationsStats gcsOperationStats;
48+
private final OperationPurpose operationPurpose;
4649
private final List<HttpRequestTracker> trackers;
4750

48-
GoogleCloudStorageHttpStatsCollector(final GoogleCloudStorageOperationsStats gcsOperationStats) {
51+
GoogleCloudStorageHttpStatsCollector(final GoogleCloudStorageOperationsStats gcsOperationStats, OperationPurpose operationPurpose) {
4952
this.gcsOperationStats = gcsOperationStats;
53+
this.operationPurpose = operationPurpose;
5054
this.trackers = trackerFactories.stream()
5155
.map(trackerFactory -> trackerFactory.apply(gcsOperationStats.getTrackedBucket()))
5256
.toList();

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

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.apache.logging.log4j.LogManager;
2626
import org.apache.logging.log4j.Logger;
2727
import org.elasticsearch.common.Strings;
28+
import org.elasticsearch.common.blobstore.OperationPurpose;
2829
import org.elasticsearch.common.util.Maps;
2930
import org.elasticsearch.core.Nullable;
3031
import org.elasticsearch.core.SuppressForbidden;
@@ -40,6 +41,7 @@
4041
import java.net.URL;
4142
import java.security.KeyStore;
4243
import java.util.Map;
44+
import java.util.stream.Collectors;
4345

4446
import static java.nio.charset.StandardCharsets.UTF_8;
4547
import static java.util.Collections.emptyMap;
@@ -51,12 +53,15 @@ public class GoogleCloudStorageService {
5153

5254
private volatile Map<String, GoogleCloudStorageClientSettings> clientSettings = emptyMap();
5355

56+
private record ClientKey(OperationPurpose purpose, String repositoryName) {}
57+
5458
/**
5559
* Dictionary of client instances. Client instances are built lazily from the
56-
* latest settings. Each repository has its own client instance identified by
57-
* the repository name.
60+
* latest settings. Clients are cached by a composite OperationPurpose/repositoryName
61+
* key.
62+
* @see ClientKey
5863
*/
59-
private volatile Map<String, Storage> clientCache = emptyMap();
64+
private volatile Map<ClientKey, Storage> clientCache = emptyMap();
6065

6166
/**
6267
* Refreshes the client settings and clears the client cache. Subsequent calls to
@@ -79,20 +84,26 @@ public synchronized void refreshAndClearCache(Map<String, GoogleCloudStorageClie
7984
*
8085
* @param clientName name of the client settings used to create the client
8186
* @param repositoryName name of the repository that would use the client
87+
* @param operationPurpose the purpose for which the client will be used
8288
* @param stats the stats collector used to gather information about the underlying SKD API calls.
8389
* @return a cached client storage instance that can be used to manage objects
8490
* (blobs)
8591
*/
86-
public Storage client(final String clientName, final String repositoryName, final GoogleCloudStorageOperationsStats stats)
87-
throws IOException {
92+
public Storage client(
93+
final String clientName,
94+
final String repositoryName,
95+
final OperationPurpose operationPurpose,
96+
final GoogleCloudStorageOperationsStats stats
97+
) throws IOException {
98+
ClientKey clientKey = new ClientKey(operationPurpose, repositoryName);
8899
{
89-
final Storage storage = clientCache.get(repositoryName);
100+
final Storage storage = clientCache.get(clientKey);
90101
if (storage != null) {
91102
return storage;
92103
}
93104
}
94105
synchronized (this) {
95-
final Storage existing = clientCache.get(repositoryName);
106+
final Storage existing = clientCache.get(clientKey);
96107

97108
if (existing != null) {
98109
return existing;
@@ -110,26 +121,33 @@ public Storage client(final String clientName, final String repositoryName, fina
110121
}
111122

112123
logger.debug(() -> format("creating GCS client with client_name [%s], endpoint [%s]", clientName, settings.getHost()));
113-
final Storage storage = createClient(settings, stats);
114-
clientCache = Maps.copyMapWithAddedEntry(clientCache, repositoryName, storage);
124+
final Storage storage = createClient(settings, stats, operationPurpose);
125+
clientCache = Maps.copyMapWithAddedEntry(clientCache, clientKey, storage);
115126
return storage;
116127
}
117128
}
118129

119-
synchronized void closeRepositoryClient(String repositoryName) {
120-
clientCache = Maps.copyMapWithRemovedEntry(clientCache, repositoryName);
130+
synchronized void closeRepositoryClients(String repositoryName) {
131+
clientCache = clientCache.entrySet()
132+
.stream()
133+
.filter(entry -> entry.getKey().repositoryName().equals(repositoryName) == false)
134+
.collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue));
121135
}
122136

123137
/**
124138
* Creates a client that can be used to manage Google Cloud Storage objects. The client is thread-safe.
125139
*
126140
* @param gcsClientSettings client settings to use, including secure settings
127141
* @param stats the stats collector to use by the underlying SDK
142+
* @param operationPurpose the purpose this client will be used for
128143
* @return a new client storage instance that can be used to manage objects
129144
* (blobs)
130145
*/
131-
private Storage createClient(GoogleCloudStorageClientSettings gcsClientSettings, GoogleCloudStorageOperationsStats stats)
132-
throws IOException {
146+
private Storage createClient(
147+
GoogleCloudStorageClientSettings gcsClientSettings,
148+
GoogleCloudStorageOperationsStats stats,
149+
OperationPurpose operationPurpose
150+
) throws IOException {
133151
final HttpTransport httpTransport = SocketAccess.doPrivilegedIOException(() -> {
134152
final NetHttpTransport.Builder builder = new NetHttpTransport.Builder();
135153
// requires java.lang.RuntimePermission "setFactory"
@@ -149,7 +167,7 @@ private Storage createClient(GoogleCloudStorageClientSettings gcsClientSettings,
149167
return builder.build();
150168
});
151169

152-
final GoogleCloudStorageHttpStatsCollector httpStatsCollector = new GoogleCloudStorageHttpStatsCollector(stats);
170+
final GoogleCloudStorageHttpStatsCollector httpStatsCollector = new GoogleCloudStorageHttpStatsCollector(stats, operationPurpose);
153171

154172
final HttpTransportOptions httpTransportOptions = new HttpTransportOptions(
155173
HttpTransportOptions.newBuilder()

0 commit comments

Comments
 (0)