@@ -58,6 +58,10 @@ const (
58
58
clusterCacheControllerName = "cluster-cache-tracker"
59
59
)
60
60
61
+ // ErrClusterLocked is returned in methods that require cluster-level locking
62
+ // if the cluster is already locked by another concurrent call.
63
+ var ErrClusterLocked = errors .New ("cluster is locked already" )
64
+
61
65
// ClusterCacheTracker manages client caches for workload clusters.
62
66
type ClusterCacheTracker struct {
63
67
log logr.Logger
@@ -67,10 +71,10 @@ type ClusterCacheTracker struct {
67
71
68
72
// clusterAccessorsLock is used to lock the access to the clusterAccessors map.
69
73
clusterAccessorsLock sync.RWMutex
70
- // clusterAccessors is the map of clusterAccessor by cluster.
74
+ // clusterAccessors is the map of clusterAccessors by cluster.
71
75
clusterAccessors map [client.ObjectKey ]* clusterAccessor
72
- // clusterLock is a per-cluster lock used whenever we lock per-cluster actions
73
- // like creating a client or adding watches.
76
+ // clusterLock is a per-cluster lock used whenever we're locking for a specific cluster.
77
+ // E.g. for actions like creating a client or adding watches.
74
78
clusterLock * keyedMutex
75
79
76
80
indexes []Index
@@ -178,6 +182,23 @@ func (t *ClusterCacheTracker) clusterAccessorExists(cluster client.ObjectKey) bo
178
182
return exists
179
183
}
180
184
185
+ // loadAccessor loads a clusterAccessor.
186
+ func (t * ClusterCacheTracker ) loadAccessor (cluster client.ObjectKey ) (* clusterAccessor , bool ) {
187
+ t .clusterAccessorsLock .RLock ()
188
+ defer t .clusterAccessorsLock .RUnlock ()
189
+
190
+ accessor , ok := t .clusterAccessors [cluster ]
191
+ return accessor , ok
192
+ }
193
+
194
+ // storeAccessor stores a clusterAccessor.
195
+ func (t * ClusterCacheTracker ) storeAccessor (cluster client.ObjectKey , accessor * clusterAccessor ) {
196
+ t .clusterAccessorsLock .Lock ()
197
+ defer t .clusterAccessorsLock .Unlock ()
198
+
199
+ t .clusterAccessors [cluster ] = accessor
200
+ }
201
+
181
202
// getClusterAccessor returns a clusterAccessor for cluster.
182
203
// It first tries to return an already-created clusterAccessor.
183
204
// It then falls back to create a new clusterAccessor if needed.
@@ -186,49 +207,36 @@ func (t *ClusterCacheTracker) clusterAccessorExists(cluster client.ObjectKey) bo
186
207
func (t * ClusterCacheTracker ) getClusterAccessor (ctx context.Context , cluster client.ObjectKey , indexes ... Index ) (* clusterAccessor , error ) {
187
208
log := ctrl .LoggerFrom (ctx , "cluster" , klog .KRef (cluster .Namespace , cluster .Name ))
188
209
189
- loadExistingAccessor := func () * clusterAccessor {
190
- t .clusterAccessorsLock .RLock ()
191
- defer t .clusterAccessorsLock .RUnlock ()
192
- return t .clusterAccessors [cluster ]
193
- }
194
- storeAccessor := func (a * clusterAccessor ) {
195
- t .clusterAccessorsLock .Lock ()
196
- defer t .clusterAccessorsLock .Unlock ()
197
- t .clusterAccessors [cluster ] = a
198
- }
199
-
200
210
// If the clusterAccessor already exists, return early.
201
- a := loadExistingAccessor ()
202
- if a != nil {
203
- return a , nil
211
+ if accessor , ok := t .loadAccessor (cluster ); ok {
212
+ return accessor , nil
204
213
}
205
214
206
215
// clusterAccessor doesn't exist yet, we might have to initialize one.
207
216
// Lock on the cluster to ensure only one clusterAccessor is initialized
208
217
// for the cluster at the same time.
209
218
// Return an error if another go routine already tries to create a clusterAccessor.
210
- unlockCluster , ok := t .clusterLock .TryLock (cluster )
211
- if ! ok {
212
- return nil , errors .Errorf ("error creating new cluster accessor: another go routine is already trying to create the cluster accessor for this cluster" )
219
+ if ok := t .clusterLock .TryLock (cluster ); ! ok {
220
+ return nil , errors .Wrapf (ErrClusterLocked , "failed to create cluster accessor: failed to get lock for cluster" )
213
221
}
214
- defer unlockCluster ( )
222
+ defer t . clusterLock . Unlock ( cluster )
215
223
216
224
// Until we got the cluster lock a different goroutine might have initialized the clusterAccessor
217
225
// for this cluster successfully already. If this is the case we return it.
218
- a = loadExistingAccessor ()
219
- if a != nil {
220
- return a , nil
226
+ if accessor , ok := t .loadAccessor (cluster ); ok {
227
+ return accessor , nil
221
228
}
222
229
223
230
// We are the go routine who has to initialize the clusterAccessor.
224
231
log .V (4 ).Info ("Creating new cluster accessor" )
225
- a , err := t .newClusterAccessor (ctx , cluster , indexes ... )
232
+ accessor , err := t .newClusterAccessor (ctx , cluster , indexes ... )
226
233
if err != nil {
227
- return nil , errors .Wrap (err , "error creating new cluster accessor" )
234
+ return nil , errors .Wrap (err , "failed to create cluster accessor" )
228
235
}
236
+
229
237
log .V (4 ).Info ("Storing new cluster accessor" )
230
- storeAccessor (a )
231
- return a , nil
238
+ t . storeAccessor (cluster , accessor )
239
+ return accessor , nil
232
240
}
233
241
234
242
// newClusterAccessor creates a new clusterAccessor.
@@ -435,11 +443,11 @@ func (t *ClusterCacheTracker) Watch(ctx context.Context, input WatchInput) error
435
443
}
436
444
437
445
// We have to lock the cluster, so that the watch is not created multiple times in parallel.
438
- unlock , ok := t .clusterLock .TryLock (input .Cluster )
446
+ ok := t .clusterLock .TryLock (input .Cluster )
439
447
if ! ok {
440
- return errors .Errorf ( "failed to add watch: another go routine is already trying to create the cluster accessor " )
448
+ return errors .Wrapf ( ErrClusterLocked , "failed to add watch: error getting lock for cluster" )
441
449
}
442
- defer unlock ( )
450
+ defer t . clusterLock . Unlock ( input . Cluster )
443
451
444
452
if a .watches .Has (input .Name ) {
445
453
t .log .V (6 ).Info ("Watch already exists" , "Cluster" , klog .KRef (input .Cluster .Namespace , input .Cluster .Name ), "name" , input .Name )
@@ -518,7 +526,7 @@ func (t *ClusterCacheTracker) healthCheckCluster(ctx context.Context, in *health
518
526
return false , nil
519
527
}
520
528
521
- if ! t . clusterAccessorExists (in .cluster ) {
529
+ if _ , ok := t . loadAccessor (in .cluster ); ! ok {
522
530
// Cache for this cluster has already been cleaned up.
523
531
// Nothing to do, so return true.
524
532
return true , nil
0 commit comments