sigs.k8s.io/kueue@v0.6.2/pkg/controller/core/leader_aware_reconciler.go (about)

     1  /*
     2  Copyright 2024 The Kubernetes 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 core
    18  
    19  import (
    20  	"context"
    21  	"time"
    22  
    23  	"k8s.io/utils/ptr"
    24  	ctrl "sigs.k8s.io/controller-runtime"
    25  	"sigs.k8s.io/controller-runtime/pkg/client"
    26  	"sigs.k8s.io/controller-runtime/pkg/reconcile"
    27  
    28  	config "sigs.k8s.io/kueue/apis/config/v1beta1"
    29  )
    30  
    31  // WithLeadingManager returns a decorating reconcile.Reconciler that discards reconciliation requests
    32  // for the controllers that are started with the controller.Options.NeedLeaderElection
    33  // option set to false in non-leading replicas.
    34  //
    35  // Starting controllers in non-leading replicas is needed for these that update the data
    36  // served by the visibility extension API server.
    37  //
    38  // This enables to:
    39  //   - Keep the scheduling decisions under the responsibility of the leading replica alone,
    40  //     to prevent any concurrency issues.
    41  //   - Consume requests from the watch event queues, to prevent them from growing indefinitely
    42  //     in the non-leading replicas.
    43  //   - Transition to actually reconciling requests in the replica that may acquire
    44  //     the leader election lease, in case the previously leading replica failed to renew it.
    45  func WithLeadingManager(mgr ctrl.Manager, reconciler reconcile.Reconciler, obj client.Object, cfg *config.Configuration) reconcile.Reconciler {
    46  	// Do not decorate the reconciler if leader election is disabled
    47  	if cfg.LeaderElection == nil || !ptr.Deref(cfg.LeaderElection.LeaderElect, false) {
    48  		return reconciler
    49  	}
    50  
    51  	return &leaderAwareReconciler{
    52  		elected:         mgr.Elected(),
    53  		client:          mgr.GetClient(),
    54  		delegate:        reconciler,
    55  		object:          obj,
    56  		requeueDuration: cfg.LeaderElection.LeaseDuration.Duration,
    57  	}
    58  }
    59  
    60  type leaderAwareReconciler struct {
    61  	elected  <-chan struct{}
    62  	client   client.Client
    63  	delegate reconcile.Reconciler
    64  	object   client.Object
    65  	// the duration used by non-leading replicas to requeue events,
    66  	// so no events are missed over the period it takes for
    67  	// leader election to fail over a new replica.
    68  	requeueDuration time.Duration
    69  }
    70  
    71  var _ reconcile.Reconciler = (*leaderAwareReconciler)(nil)
    72  
    73  func (r *leaderAwareReconciler) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) {
    74  	select {
    75  	case <-r.elected:
    76  		// The manager has been elected leader, delegate reconciliation to the provided reconciler.
    77  		return r.delegate.Reconcile(ctx, request)
    78  	default:
    79  		if err := r.client.Get(ctx, request.NamespacedName, r.object); err != nil {
    80  			// Discard request if not found, to prevent from re-enqueueing indefinitely.
    81  			return ctrl.Result{}, client.IgnoreNotFound(err)
    82  		}
    83  		// The manager hasn't been elected leader yet, requeue the reconciliation request
    84  		// to prevent against any missed / discarded events over the period it takes
    85  		// to fail over a new leading replica, which can take as much as the configured
    86  		// lease duration, for it to acquire leadership.
    87  		return ctrl.Result{RequeueAfter: r.requeueDuration}, nil
    88  	}
    89  }