github.com/cilium/cilium@v1.16.2/operator/pkg/ciliumendpointslice/rate_limit.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package ciliumendpointslice 5 6 import ( 7 "encoding/json" 8 "fmt" 9 "sort" 10 "strings" 11 "time" 12 13 "github.com/sirupsen/logrus" 14 "golang.org/x/time/rate" 15 "k8s.io/client-go/util/workqueue" 16 17 "github.com/cilium/cilium/pkg/logging/logfields" 18 ) 19 20 type rateLimit struct { 21 Nodes int 22 Limit float64 23 Burst int 24 } 25 26 type rateLimitConfig struct { 27 current rateLimit 28 dynamicRateLimit dynamicRateLimit 29 30 rateLimiter *workqueue.BucketRateLimiter 31 32 logger logrus.FieldLogger 33 } 34 35 type dynamicRateLimit []rateLimit 36 37 func getRateLimitConfig(p params) (rateLimitConfig, error) { 38 rlc := rateLimitConfig{ 39 logger: p.Logger, 40 } 41 parsed, err := parseDynamicRateLimit(p.Cfg.CESDynamicRateLimitConfig) 42 if err != nil { 43 return rlc, fmt.Errorf("Couldn't parse CES rate limit config: %w", err) 44 } 45 rlc.dynamicRateLimit = parsed 46 rlc.updateRateLimitWithNodes(0, true) 47 rlc.rateLimiter = &workqueue.BucketRateLimiter{Limiter: rate.NewLimiter(rate.Limit(rlc.current.Limit), rlc.current.Burst)} 48 return rlc, nil 49 } 50 51 func parseDynamicRateLimit(cfg string) (dynamicRateLimit, error) { 52 if len(cfg) == 0 { 53 return nil, fmt.Errorf("invalid: config is empty") 54 } 55 dynamicRateLimit := dynamicRateLimit{} 56 decoder := json.NewDecoder(strings.NewReader(cfg)) 57 decoder.DisallowUnknownFields() 58 59 if err := decoder.Decode(&dynamicRateLimit); err != nil { 60 return nil, err 61 } 62 63 sort.Slice(dynamicRateLimit, func(i, j int) bool { 64 return dynamicRateLimit[i].Nodes < dynamicRateLimit[j].Nodes 65 }) 66 return dynamicRateLimit, nil 67 } 68 69 func (rlc *rateLimitConfig) getDelay() time.Duration { 70 return rlc.rateLimiter.Reserve().Delay() 71 } 72 73 func (rlc *rateLimitConfig) updateRateLimiterWithNodes(nodes int) bool { 74 changed := rlc.updateRateLimitWithNodes(nodes, false) 75 if changed { 76 rlc.logger.WithFields(logrus.Fields{ 77 "nodes": nodes, 78 logfields.WorkQueueQPSLimit: rlc.current.Limit, 79 logfields.WorkQueueBurstLimit: rlc.current.Burst, 80 }).Info("Updating rate limit") 81 82 rlc.rateLimiter.SetBurst(rlc.current.Burst) 83 rlc.rateLimiter.SetLimit(rate.Limit(rlc.current.Limit)) 84 } 85 return changed 86 } 87 88 func (rlc *rateLimitConfig) updateRateLimitWithNodes(nodes int, force bool) bool { 89 index := 0 90 for ; index < len(rlc.dynamicRateLimit)-1; index++ { 91 if rlc.dynamicRateLimit[index+1].Nodes > nodes { 92 break 93 } 94 } 95 changed := rlc.current.Nodes != rlc.dynamicRateLimit[index].Nodes 96 97 if changed || force { 98 rlc.current = rateLimit{ 99 Nodes: rlc.dynamicRateLimit[index].Nodes, 100 Limit: rlc.dynamicRateLimit[index].Limit, 101 Burst: rlc.dynamicRateLimit[index].Burst, 102 } 103 if rlc.current.Limit > CESWriteQPSLimitMax { 104 rlc.current.Limit = CESWriteQPSLimitMax 105 } 106 if rlc.current.Burst > CESWriteQPSBurstMax { 107 rlc.current.Burst = CESWriteQPSBurstMax 108 } 109 return true 110 } 111 return false 112 }