knative.dev/pkg@v0.0.0-20260602142205-ac97e43f6622/reconciler/leader.go (about)

     1  /*
     2  Copyright 2020 The Knative Authors
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package reconciler
    18  
    19  import (
    20  	"sync"
    21  
    22  	"k8s.io/apimachinery/pkg/types"
    23  )
    24  
    25  // Bucket is an opaque type used to scope leadership.
    26  type Bucket interface {
    27  	// Name returns a string representing this bucket, which uniquely
    28  	// identifies the bucket and is suitable for use as a resource lock name.
    29  	Name() string
    30  
    31  	// Has determines whether this Bucket contains a particular key.
    32  	Has(key types.NamespacedName) bool
    33  }
    34  
    35  // UniversalBucket returns a Bucket that "Has()" all keys.
    36  func UniversalBucket() Bucket {
    37  	return &bucket{}
    38  }
    39  
    40  type bucket struct{}
    41  
    42  var _ Bucket = (*bucket)(nil)
    43  
    44  // Name implements Bucket
    45  func (b *bucket) Name() string {
    46  	return ""
    47  }
    48  
    49  // Has implements Bucket
    50  func (b *bucket) Has(nn types.NamespacedName) bool {
    51  	return true
    52  }
    53  
    54  // LeaderAware is implemented by Reconcilers that are aware of their leader status.
    55  type LeaderAware interface {
    56  	// Promote is called when we become the leader of a given Bucket.  It must be
    57  	// supplied with an enqueue function through which a Bucket resync may be triggered.
    58  	Promote(b Bucket, enq func(Bucket, types.NamespacedName)) error
    59  
    60  	// Demote is called when we stop being the leader for the specified Bucket.
    61  	Demote(Bucket)
    62  }
    63  
    64  // LeaderAwareFuncs implements LeaderAware using the given functions for handling
    65  // promotion and demotion.
    66  type LeaderAwareFuncs struct {
    67  	sync.RWMutex
    68  	buckets map[string]Bucket
    69  
    70  	PromoteFunc func(b Bucket, enq func(Bucket, types.NamespacedName)) error
    71  	DemoteFunc  func(b Bucket)
    72  }
    73  
    74  var _ LeaderAware = (*LeaderAwareFuncs)(nil)
    75  
    76  // IsLeaderFor implements LeaderAware
    77  func (laf *LeaderAwareFuncs) IsLeaderFor(key types.NamespacedName) bool {
    78  	laf.RLock()
    79  	defer laf.RUnlock()
    80  
    81  	for _, bkt := range laf.buckets {
    82  		if bkt.Has(key) {
    83  			return true
    84  		}
    85  	}
    86  	return false
    87  }
    88  
    89  // Promote implements LeaderAware
    90  func (laf *LeaderAwareFuncs) Promote(b Bucket, enq func(Bucket, types.NamespacedName)) error {
    91  	func() {
    92  		laf.Lock()
    93  		defer laf.Unlock()
    94  		if laf.buckets == nil {
    95  			laf.buckets = make(map[string]Bucket, 1)
    96  		}
    97  		laf.buckets[b.Name()] = b
    98  	}()
    99  
   100  	if promote := laf.PromoteFunc; promote != nil && enq != nil {
   101  		return promote(b, enq)
   102  	}
   103  	return nil
   104  }
   105  
   106  // Demote implements LeaderAware
   107  func (laf *LeaderAwareFuncs) Demote(b Bucket) {
   108  	func() {
   109  		laf.Lock()
   110  		defer laf.Unlock()
   111  		delete(laf.buckets, b.Name())
   112  	}()
   113  
   114  	if demote := laf.DemoteFunc; demote != nil {
   115  		demote(b)
   116  	}
   117  }