@@ -41,8 +41,9 @@ public class GoogleCloudStorageClientsManager implements ClusterStateApplier {
4141 GcsRepositoryStatsCollector ,
4242 MeteredStorage ,
4343 IOException > clientBuilder ;
44- private final ClusterClientsHolder clusterClientsHolder = new ClusterClientsHolder ();
45- private final Map <ProjectId , ClientsHolder > projectClientHolders ;
44+ // A map of projectId to clients holder. Adding to and removing from the map happen only in the applier thread.
45+ private final Map <ProjectId , ClientsHolder > perProjectClientsHolders ;
46+ private final ClusterClientsHolder clusterClientsHolder ;
4647
4748 public GoogleCloudStorageClientsManager (
4849 Settings nodeSettings ,
@@ -55,15 +56,16 @@ public GoogleCloudStorageClientsManager(
5556 .build ();
5657 this .clientBuilder = clientBuilder ;
5758 if (supportsMultipleProjects ) {
58- this .projectClientHolders = ConcurrentCollections .newConcurrentMap ();
59+ this .perProjectClientsHolders = ConcurrentCollections .newConcurrentMap ();
5960 } else {
60- this .projectClientHolders = null ;
61+ this .perProjectClientsHolders = null ;
6162 }
63+ this .clusterClientsHolder = new ClusterClientsHolder ();
6264 }
6365
6466 @ Override
6567 public void applyClusterState (ClusterChangedEvent event ) {
66- assert projectClientHolders != null ;
68+ assert perProjectClientsHolders != null ;
6769 final Map <ProjectId , ProjectMetadata > currentProjects = event .state ().metadata ().projects ();
6870
6971 final var updatedPerProjectClients = new HashMap <ProjectId , ClientsHolder >();
@@ -77,7 +79,7 @@ public void applyClusterState(ClusterChangedEvent event) {
7779 // Project secrets can be null when node restarts. It may not have any s3 credentials if s3 is not in use.
7880 if (projectSecrets == null || projectSecrets .getSettingNames ().stream ().noneMatch (key -> key .startsWith (GCS_SETTING_PREFIX ))) {
7981 // Most likely there won't be any existing client, but attempt to remove it anyway just in case
80- projectClientHolders .remove (project .id ());
82+ perProjectClientsHolders .remove (project .id ());
8183 continue ;
8284 }
8385
@@ -113,13 +115,13 @@ public void applyClusterState(ClusterChangedEvent event) {
113115 // Updated projects
114116 for (var projectId : updatedPerProjectClients .keySet ()) {
115117 assert ProjectId .DEFAULT .equals (projectId ) == false ;
116- projectClientHolders .put (projectId , updatedPerProjectClients .get (projectId ));
118+ perProjectClientsHolders .put (projectId , updatedPerProjectClients .get (projectId ));
117119 }
118120 // Removed projects
119- for (var projectId : projectClientHolders .keySet ()) {
121+ for (var projectId : perProjectClientsHolders .keySet ()) {
120122 if (currentProjects .containsKey (projectId ) == false ) {
121123 assert ProjectId .DEFAULT .equals (projectId ) == false ;
122- projectClientHolders .remove (projectId );
124+ perProjectClientsHolders .remove (projectId );
123125 }
124126 }
125127 }
@@ -140,23 +142,41 @@ void refreshAndClearCacheForClusterClients(Map<String, GoogleCloudStorageClientS
140142
141143 MeteredStorage client (ProjectId projectId , String clientName , String repositoryName , GcsRepositoryStatsCollector statsCollector )
142144 throws IOException {
143- return getClientsHolderSafe (projectId ).client (clientName , repositoryName , statsCollector );
145+ if (projectId == null || ProjectId .DEFAULT .equals (projectId )) {
146+ return clusterClientsHolder .client (clientName , repositoryName , statsCollector );
147+ } else {
148+ return getClientsHolderSafe (projectId ).client (clientName , repositoryName , statsCollector );
149+ }
144150 }
145151
146152 void closeRepositoryClients (ProjectId projectId , String repositoryName ) {
147- getClientsHolderSafe (projectId ).closeRepositoryClients (repositoryName );
153+ if (projectId == null || ProjectId .DEFAULT .equals (projectId )) {
154+ clusterClientsHolder .closeRepositoryClients (repositoryName );
155+ } else {
156+ getClientsHolderSafe (projectId ).closeRepositoryClients (repositoryName );
157+ }
158+ }
159+
160+ // package private for tests
161+ ClusterClientsHolder getClusterClientsHolder () {
162+ return clusterClientsHolder ;
163+ }
164+
165+ // package private for tests
166+ Map <ProjectId , ClientsHolder > getPerProjectClientsHolders () {
167+ return perProjectClientsHolders == null ? null : Map .copyOf (perProjectClientsHolders );
148168 }
149169
150170 private boolean newOrUpdated (ProjectId projectId , Map <String , GoogleCloudStorageClientSettings > currentClientSettings ) {
151- final var old = projectClientHolders .get (projectId );
171+ final var old = perProjectClientsHolders .get (projectId );
152172 if (old == null ) {
153173 return true ;
154174 }
155- return currentClientSettings .equals (old .getClientSettings ()) == false ;
175+ return currentClientSettings .equals (old .allClientSettings ()) == false ;
156176 }
157177
158178 private ClientsHolder getClientsHolderSafe (ProjectId projectId ) {
159- final var clientsHolder = projectClientHolders .get (projectId );
179+ final var clientsHolder = perProjectClientsHolders .get (projectId );
160180 if (clientsHolder == null ) {
161181 assert ProjectId .DEFAULT .equals (projectId ) == false ;
162182 throw new IllegalArgumentException ("No GCS client is configured for project [" + projectId + "]" );
@@ -175,7 +195,7 @@ abstract class ClientsHolder {
175195 /**
176196 * Get the current client settings for all clients in this holder.
177197 */
178- protected abstract Map <String , GoogleCloudStorageClientSettings > getClientSettings ();
198+ protected abstract Map <String , GoogleCloudStorageClientSettings > allClientSettings ();
179199
180200 /**
181201 * Attempts to retrieve a client from the cache. If the client does not exist it
@@ -204,14 +224,14 @@ MeteredStorage client(final String clientName, final String repositoryName, fina
204224 return existing ;
205225 }
206226
207- final GoogleCloudStorageClientSettings settings = getClientSettings ().get (clientName );
227+ final GoogleCloudStorageClientSettings settings = allClientSettings ().get (clientName );
208228
209229 if (settings == null ) {
210230 throw new IllegalArgumentException (
211231 "Unknown client name ["
212232 + clientName
213233 + "]. Existing client configs: "
214- + Strings .collectionToDelimitedString (getClientSettings ().keySet (), "," )
234+ + Strings .collectionToDelimitedString (allClientSettings ().keySet (), "," )
215235 );
216236 }
217237
@@ -228,14 +248,19 @@ synchronized void closeRepositoryClients(String repositoryName) {
228248 .filter (entry -> entry .getKey ().equals (repositoryName ) == false )
229249 .collect (Collectors .toUnmodifiableMap (Map .Entry ::getKey , java .util .Map .Entry ::getValue ));
230250 }
251+
252+ // package private for tests
253+ final boolean hasCachedClientForRepository (String repositoryName ) {
254+ return clientCache .containsKey (repositoryName );
255+ }
231256 }
232257
233258 final class ClusterClientsHolder extends ClientsHolder {
234259
235260 private volatile Map <String , GoogleCloudStorageClientSettings > clientSettings = emptyMap ();
236261
237262 @ Override
238- protected Map <String , GoogleCloudStorageClientSettings > getClientSettings () {
263+ protected Map <String , GoogleCloudStorageClientSettings > allClientSettings () {
239264 return clientSettings ;
240265 }
241266
@@ -261,7 +286,7 @@ final class PerProjectClientsHolder extends ClientsHolder {
261286 }
262287
263288 @ Override
264- protected Map <String , GoogleCloudStorageClientSettings > getClientSettings () {
289+ protected Map <String , GoogleCloudStorageClientSettings > allClientSettings () {
265290 return clientSettings ;
266291 }
267292 }
0 commit comments