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 }