k8s.io/apiserver@v0.29.3/pkg/storage/etcd3/lease_manager.go (about)

     1  /*
     2  Copyright 2018 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 etcd3
    18  
    19  import (
    20  	"context"
    21  	"sync"
    22  	"time"
    23  
    24  	clientv3 "go.etcd.io/etcd/client/v3"
    25  	"k8s.io/apiserver/pkg/storage/etcd3/metrics"
    26  )
    27  
    28  const (
    29  	defaultLeaseReuseDurationSeconds = 60
    30  	defaultLeaseMaxObjectCount       = 1000
    31  )
    32  
    33  // LeaseManagerConfig is configuration for creating a lease manager.
    34  type LeaseManagerConfig struct {
    35  	// ReuseDurationSeconds specifies time in seconds that each lease is reused
    36  	ReuseDurationSeconds int64
    37  	// MaxObjectCount specifies how many objects that a lease can attach
    38  	MaxObjectCount int64
    39  }
    40  
    41  // NewDefaultLeaseManagerConfig creates a LeaseManagerConfig with default values
    42  func NewDefaultLeaseManagerConfig() LeaseManagerConfig {
    43  	return LeaseManagerConfig{
    44  		ReuseDurationSeconds: defaultLeaseReuseDurationSeconds,
    45  		MaxObjectCount:       defaultLeaseMaxObjectCount,
    46  	}
    47  }
    48  
    49  // leaseManager is used to manage leases requested from etcd. If a new write
    50  // needs a lease that has similar expiration time to the previous one, the old
    51  // lease will be reused to reduce the overhead of etcd, since lease operations
    52  // are expensive. In the implementation, we only store one previous lease,
    53  // since all the events have the same ttl.
    54  type leaseManager struct {
    55  	client                  *clientv3.Client // etcd client used to grant leases
    56  	leaseMu                 sync.Mutex
    57  	prevLeaseID             clientv3.LeaseID
    58  	prevLeaseExpirationTime time.Time
    59  	// The period of time in seconds and percent of TTL that each lease is
    60  	// reused. The minimum of them is used to avoid unreasonably large
    61  	// numbers.
    62  	leaseReuseDurationSeconds   int64
    63  	leaseReuseDurationPercent   float64
    64  	leaseMaxAttachedObjectCount int64
    65  	leaseAttachedObjectCount    int64
    66  }
    67  
    68  // newDefaultLeaseManager creates a new lease manager using default setting.
    69  func newDefaultLeaseManager(client *clientv3.Client, config LeaseManagerConfig) *leaseManager {
    70  	if config.MaxObjectCount <= 0 {
    71  		config.MaxObjectCount = defaultLeaseMaxObjectCount
    72  	}
    73  	return newLeaseManager(client, config.ReuseDurationSeconds, 0.05, config.MaxObjectCount)
    74  }
    75  
    76  // newLeaseManager creates a new lease manager with the number of buffered
    77  // leases, lease reuse duration in seconds and percentage. The percentage
    78  // value x means x*100%.
    79  func newLeaseManager(client *clientv3.Client, leaseReuseDurationSeconds int64, leaseReuseDurationPercent float64, maxObjectCount int64) *leaseManager {
    80  	return &leaseManager{
    81  		client:                      client,
    82  		leaseReuseDurationSeconds:   leaseReuseDurationSeconds,
    83  		leaseReuseDurationPercent:   leaseReuseDurationPercent,
    84  		leaseMaxAttachedObjectCount: maxObjectCount,
    85  	}
    86  }
    87  
    88  // GetLease returns a lease based on requested ttl: if the cached previous
    89  // lease can be reused, reuse it; otherwise request a new one from etcd.
    90  func (l *leaseManager) GetLease(ctx context.Context, ttl int64) (clientv3.LeaseID, error) {
    91  	now := time.Now()
    92  	l.leaseMu.Lock()
    93  	defer l.leaseMu.Unlock()
    94  	// check if previous lease can be reused
    95  	reuseDurationSeconds := l.getReuseDurationSecondsLocked(ttl)
    96  	valid := now.Add(time.Duration(ttl) * time.Second).Before(l.prevLeaseExpirationTime)
    97  	sufficient := now.Add(time.Duration(ttl+reuseDurationSeconds) * time.Second).After(l.prevLeaseExpirationTime)
    98  
    99  	// We count all operations that happened in the same lease, regardless of success or failure.
   100  	// Currently each GetLease call only attach 1 object
   101  	l.leaseAttachedObjectCount++
   102  
   103  	if valid && sufficient && l.leaseAttachedObjectCount <= l.leaseMaxAttachedObjectCount {
   104  		return l.prevLeaseID, nil
   105  	}
   106  
   107  	// request a lease with a little extra ttl from etcd
   108  	ttl += reuseDurationSeconds
   109  	lcr, err := l.client.Lease.Grant(ctx, ttl)
   110  	if err != nil {
   111  		return clientv3.LeaseID(0), err
   112  	}
   113  	// cache the new lease id
   114  	l.prevLeaseID = lcr.ID
   115  	l.prevLeaseExpirationTime = now.Add(time.Duration(ttl) * time.Second)
   116  	// refresh count
   117  	metrics.UpdateLeaseObjectCount(l.leaseAttachedObjectCount)
   118  	l.leaseAttachedObjectCount = 1
   119  	return lcr.ID, nil
   120  }
   121  
   122  // getReuseDurationSecondsLocked returns the reusable duration in seconds
   123  // based on the configuration. Lock has to be acquired before calling this
   124  // function.
   125  func (l *leaseManager) getReuseDurationSecondsLocked(ttl int64) int64 {
   126  	reuseDurationSeconds := int64(l.leaseReuseDurationPercent * float64(ttl))
   127  	if reuseDurationSeconds > l.leaseReuseDurationSeconds {
   128  		reuseDurationSeconds = l.leaseReuseDurationSeconds
   129  	}
   130  	return reuseDurationSeconds
   131  }