github.com/cilium/cilium@v1.16.2/pkg/rate/api_limiter.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package rate 5 6 import ( 7 "context" 8 "errors" 9 "fmt" 10 "math" 11 "strconv" 12 "strings" 13 14 "github.com/google/uuid" 15 "github.com/sirupsen/logrus" 16 "golang.org/x/sync/semaphore" 17 "golang.org/x/time/rate" 18 19 "github.com/cilium/cilium/pkg/lock" 20 "github.com/cilium/cilium/pkg/logging" 21 "github.com/cilium/cilium/pkg/logging/logfields" 22 "github.com/cilium/cilium/pkg/time" 23 ) 24 25 var ( 26 log = logging.DefaultLogger.WithField(logfields.LogSubsys, "rate") 27 ErrWaitCancelled = errors.New("request cancelled while waiting for rate limiting slot") 28 ) 29 30 const ( 31 defaultMeanOver = 10 32 defaultDelayedAdjustmentFactor = 0.50 33 defaultMaxAdjustmentFactor = 100.0 34 35 // waitSemaphoreWeight is the maximum resolution of the wait semaphore, 36 // the higher this value, the more accurate the ParallelRequests 37 // requirement is implemented 38 waitSemaphoreResolution = 10000000 39 40 // logUUID is the UUID of the request. 41 logUUID = "uuid" 42 // logAPICallName is the name of the underlying API call, such as 43 // "endpoint-create". 44 logAPICallName = "name" 45 // logProcessingDuration is the time taken to perform the actual underlying 46 // API call such as creating an endpoint or deleting an endpoint. This is 47 // the time between when the request has finished waiting (or being 48 // delayed), to when the underlying action has finished. 49 logProcessingDuration = "processingDuration" 50 // logParallelRequests is the number of allowed parallel requests. See 51 // APILimiter.parallelRequests. 52 logParallelRequests = "parallelRequests" 53 // logMinWaitDuration represents APILimiterParameters.MinWaitDuration. 54 logMinWaitDuration = "minWaitDuration" 55 // logMaxWaitDuration represents APILimiterParameters.MaxWaitDuration. 56 logMaxWaitDuration = "maxWaitDuration" 57 // logMaxWaitDurationLimiter is the actual / calculated maximum threshold 58 // for a request to wait. Any request exceeding this threshold will not be 59 // processed. 60 logMaxWaitDurationLimiter = "maxWaitDurationLimiter" 61 // logWaitDurationLimit is the actual / calculated amount of time 62 // determined by the underlying rate-limiting library that this request 63 // must wait before the rate limiter releases it, so that it can take the 64 // underlying action. See golang.org/x/time/rate.(*Reservation).Delay(). 65 logWaitDurationLimit = "waitDurationLimiter" 66 // logWaitDurationTotal is the actual total amount of time that this 67 // request spent waiting to be released by the rate limiter. 68 logWaitDurationTotal = "waitDurationTotal" 69 // logLimit is the rate limit. See APILimiterParameters.RateLimit. 70 logLimit = "limit" 71 // logLimit is the burst rate. See APILimiterParameters.RateBurst. 72 logBurst = "burst" 73 // logTotalDuration is the total time between when the request was first 74 // scheduled (entered the rate limiter) to when it completed processing of 75 // the underlying action. This is the absolute total time of the request 76 // from beginning to end. 77 logTotalDuration = "totalDuration" 78 // logSkipped represents whether the rate limiter will skip rate-limiting 79 // this request. See APILimiterParameters.SkipInitial. 80 logSkipped = "rateLimiterSkipped" 81 ) 82 83 type outcome string 84 85 const ( 86 outcomeParallelMaxWait outcome = "fail-parallel-wait" 87 outcomeLimitMaxWait outcome = "fail-limit-wait" 88 outcomeReqCancelled outcome = "request-cancelled" 89 outcomeErrorCode int = 429 90 outcomeSuccessCode int = 200 91 ) 92 93 // APILimiter is an extension to x/time/rate.Limiter specifically for Cilium 94 // API calls. It allows to automatically adjust the rate, burst and maximum 95 // parallel API calls to stay as close as possible to an estimated processing 96 // time. 97 type APILimiter struct { 98 // name is the name of the API call. This field is immutable after 99 // NewAPILimiter() 100 name string 101 102 // params is the parameters of the limiter. This field is immutable 103 // after NewAPILimiter() 104 params APILimiterParameters 105 106 // metrics points to the metrics implementation provided by the caller 107 // of the APILimiter. This field is immutable after NewAPILimiter() 108 metrics MetricsObserver 109 110 // mutex protects all fields below this line 111 mutex lock.RWMutex 112 113 // meanProcessingDuration is the latest mean processing duration, 114 // calculated based on processingDurations 115 meanProcessingDuration float64 116 117 // processingDurations is the last params.MeanOver processing durations 118 processingDurations []time.Duration 119 120 // meanWaitDuration is the latest mean wait duration, calculated based 121 // on waitDurations 122 meanWaitDuration float64 123 124 // waitDurations is the last params.MeanOver wait durations 125 waitDurations []time.Duration 126 127 // parallelRequests is the currently allowed maximum parallel 128 // requests. This defaults to params.MaxParallel requests and is then 129 // adjusted automatically if params.AutoAdjust is enabled. 130 parallelRequests int 131 132 // adjustmentFactor is the latest adjustment factor. It is the ratio 133 // between params.EstimatedProcessingDuration and 134 // meanProcessingDuration. 135 adjustmentFactor float64 136 137 // limiter is the rate limiter based on params.RateLimit and 138 // params.RateBurst. 139 limiter *rate.Limiter 140 141 // currentRequestsInFlight is the number of parallel API requests 142 // currently in flight 143 currentRequestsInFlight int 144 145 // requestsProcessed is the total number of processed requests 146 requestsProcessed int64 147 148 // requestsScheduled is the total number of scheduled requests 149 requestsScheduled int64 150 151 // parallelWaitSemaphore is the semaphore used to implement 152 // params.MaxParallel. It is initialized with a capacity of 153 // waitSemaphoreResolution and each API request will acquire 154 // waitSemaphoreResolution/params.MaxParallel tokens. 155 parallelWaitSemaphore *semaphore.Weighted 156 } 157 158 // APILimiterParameters is the configuration of an APILimiter. The structure 159 // may not be mutated after it has been passed into NewAPILimiter(). 160 type APILimiterParameters struct { 161 // EstimatedProcessingDuration is the estimated duration an API call 162 // will take. This value is used if AutoAdjust is enabled to 163 // automatically adjust rate limits to stay as close as possible to the 164 // estimated processing duration. 165 EstimatedProcessingDuration time.Duration 166 167 // AutoAdjust enables automatic adjustment of the values 168 // ParallelRequests, RateLimit, and RateBurst in order to keep the 169 // mean processing duration close to EstimatedProcessingDuration 170 AutoAdjust bool 171 172 // MeanOver is the number of entries to keep in order to calculate the 173 // mean processing and wait duration 174 MeanOver int 175 176 // ParallelRequests is the parallel requests allowed. If AutoAdjust is 177 // enabled, the value will adjust automatically. 178 ParallelRequests int 179 180 // MaxParallelRequests is the maximum parallel requests allowed. If 181 // AutoAdjust is enabled, then the ParalelRequests will never grow 182 // above MaxParallelRequests. 183 MaxParallelRequests int 184 185 // MinParallelRequests is the minimum parallel requests allowed. If 186 // AutoAdjust is enabled, then the ParallelRequests will never fall 187 // below MinParallelRequests. 188 MinParallelRequests int 189 190 // RateLimit is the initial number of API requests allowed per second. 191 // If AutoAdjust is enabled, the value will adjust automatically. 192 RateLimit rate.Limit 193 194 // RateBurst is the initial allowed burst of API requests allowed. If 195 // AutoAdjust is enabled, the value will adjust automatically. 196 RateBurst int 197 198 // MinWaitDuration is the minimum time an API request always has to 199 // wait before the Wait() function returns an error. 200 MinWaitDuration time.Duration 201 202 // MaxWaitDuration is the maximum time an API request is allowed to 203 // wait before the Wait() function returns an error. 204 MaxWaitDuration time.Duration 205 206 // Log enables info logging of processed API requests. This should only 207 // be used for low frequency API calls. 208 Log bool 209 210 // DelayedAdjustmentFactor is percentage of the AdjustmentFactor to be 211 // applied to RateBurst and MaxWaitDuration defined as a value between 212 // 0.0..1.0. This is used to steer a slower reaction of the RateBurst 213 // and ParallelRequests compared to RateLimit. 214 DelayedAdjustmentFactor float64 215 216 // SkipInitial is the number of initial API calls for which to not 217 // apply any rate limiting. This is useful to define a learning phase 218 // in the beginning to allow for auto adjustment before imposing wait 219 // durations and rate limiting on API calls. 220 SkipInitial int 221 222 // MaxAdjustmentFactor is the maximum adjustment factor when AutoAdjust 223 // is enabled. Base values will not adjust more than by this factor. 224 MaxAdjustmentFactor float64 225 } 226 227 // MergeUserConfig merges the provided user configuration into the existing 228 // parameters and returns a new copy. 229 func (p APILimiterParameters) MergeUserConfig(config string) (APILimiterParameters, error) { 230 if err := (&p).mergeUserConfig(config); err != nil { 231 return APILimiterParameters{}, err 232 } 233 234 return p, nil 235 } 236 237 // NewAPILimiter returns a new APILimiter based on the parameters and metrics implementation 238 func NewAPILimiter(name string, p APILimiterParameters, metrics MetricsObserver) *APILimiter { 239 if p.MeanOver == 0 { 240 p.MeanOver = defaultMeanOver 241 } 242 243 if p.MinParallelRequests == 0 { 244 p.MinParallelRequests = 1 245 } 246 247 if p.RateBurst == 0 { 248 p.RateBurst = 1 249 } 250 251 if p.DelayedAdjustmentFactor == 0.0 { 252 p.DelayedAdjustmentFactor = defaultDelayedAdjustmentFactor 253 } 254 255 if p.MaxAdjustmentFactor == 0.0 { 256 p.MaxAdjustmentFactor = defaultMaxAdjustmentFactor 257 } 258 259 l := &APILimiter{ 260 name: name, 261 params: p, 262 parallelRequests: p.ParallelRequests, 263 parallelWaitSemaphore: semaphore.NewWeighted(waitSemaphoreResolution), 264 metrics: metrics, 265 } 266 267 if p.RateLimit != 0 { 268 l.limiter = rate.NewLimiter(p.RateLimit, p.RateBurst) 269 } 270 271 return l 272 } 273 274 // NewAPILimiterFromConfig returns a new APILimiter based on user configuration 275 func NewAPILimiterFromConfig(name, config string, metrics MetricsObserver) (*APILimiter, error) { 276 p := &APILimiterParameters{} 277 278 if err := p.mergeUserConfig(config); err != nil { 279 return nil, err 280 } 281 282 return NewAPILimiter(name, *p, metrics), nil 283 } 284 285 func (p *APILimiterParameters) mergeUserConfigKeyValue(key, value string) error { 286 switch strings.ToLower(key) { 287 case "rate-limit": 288 limit, err := parseRate(value) 289 if err != nil { 290 return fmt.Errorf("unable to parse rate %q: %w", value, err) 291 } 292 p.RateLimit = limit 293 case "rate-burst": 294 burst, err := parsePositiveInt(value) 295 if err != nil { 296 return err 297 } 298 p.RateBurst = burst 299 case "min-wait-duration": 300 minWaitDuration, err := time.ParseDuration(value) 301 if err != nil { 302 return fmt.Errorf("unable to parse duration %q: %w", value, err) 303 } 304 p.MinWaitDuration = minWaitDuration 305 case "max-wait-duration": 306 maxWaitDuration, err := time.ParseDuration(value) 307 if err != nil { 308 return fmt.Errorf("unable to parse duration %q: %w", value, err) 309 } 310 p.MaxWaitDuration = maxWaitDuration 311 case "estimated-processing-duration": 312 estProcessingDuration, err := time.ParseDuration(value) 313 if err != nil { 314 return fmt.Errorf("unable to parse duration %q: %w", value, err) 315 } 316 p.EstimatedProcessingDuration = estProcessingDuration 317 case "auto-adjust": 318 v, err := strconv.ParseBool(value) 319 if err != nil { 320 return fmt.Errorf("unable to parse bool %q: %w", value, err) 321 } 322 p.AutoAdjust = v 323 case "parallel-requests": 324 parallel, err := parsePositiveInt(value) 325 if err != nil { 326 return err 327 } 328 p.ParallelRequests = parallel 329 case "min-parallel-requests": 330 minParallel, err := parsePositiveInt(value) 331 if err != nil { 332 return err 333 } 334 p.MinParallelRequests = minParallel 335 case "max-parallel-requests": 336 maxParallel, err := parsePositiveInt(value) 337 if err != nil { 338 return err 339 } 340 p.MaxParallelRequests = int(maxParallel) 341 case "mean-over": 342 meanOver, err := parsePositiveInt(value) 343 if err != nil { 344 return err 345 } 346 p.MeanOver = meanOver 347 case "log": 348 v, err := strconv.ParseBool(value) 349 if err != nil { 350 return fmt.Errorf("unable to parse bool %q: %w", value, err) 351 } 352 p.Log = v 353 case "delayed-adjustment-factor": 354 delayedAdjustmentFactor, err := strconv.ParseFloat(value, 64) 355 if err != nil { 356 return fmt.Errorf("unable to parse float %q: %w", value, err) 357 } 358 p.DelayedAdjustmentFactor = delayedAdjustmentFactor 359 case "max-adjustment-factor": 360 maxAdjustmentFactor, err := strconv.ParseFloat(value, 64) 361 if err != nil { 362 return fmt.Errorf("unable to parse float %q: %w", value, err) 363 } 364 p.MaxAdjustmentFactor = maxAdjustmentFactor 365 case "skip-initial": 366 skipInitial, err := parsePositiveInt(value) 367 if err != nil { 368 return err 369 } 370 p.SkipInitial = skipInitial 371 default: 372 return fmt.Errorf("unknown rate limiting option %q", key) 373 } 374 375 return nil 376 } 377 378 func (p *APILimiterParameters) mergeUserConfig(config string) error { 379 tokens := strings.Split(config, ",") 380 for _, token := range tokens { 381 if token == "" { 382 continue 383 } 384 385 t := strings.SplitN(token, ":", 2) 386 if len(t) != 2 { 387 return fmt.Errorf("unable to parse rate limit option %q, must in the form name=option:value[,option:value]", token) 388 } 389 390 if err := p.mergeUserConfigKeyValue(t[0], t[1]); err != nil { 391 return fmt.Errorf("unable to parse rate limit option %q with value %q: %w", t[0], t[1], err) 392 } 393 } 394 395 return nil 396 } 397 398 func (l *APILimiter) Parameters() APILimiterParameters { 399 return l.params 400 } 401 402 // SetRateLimit sets the rate limit of the limiter. If limiter is unset, a new 403 // Limiter is created using the rate burst set in the parameters. 404 func (l *APILimiter) SetRateLimit(limit rate.Limit) { 405 l.mutex.Lock() 406 defer l.mutex.Unlock() 407 if l.limiter != nil { 408 l.limiter.SetLimit(limit) 409 } else { 410 l.limiter = rate.NewLimiter(limit, l.params.RateBurst) 411 } 412 } 413 414 // SetRateBurst sets the rate burst of the limiter. If limiter is unset, a new 415 // Limiter is created using the rate limit set in the parameters. 416 func (l *APILimiter) SetRateBurst(burst int) { 417 l.mutex.Lock() 418 defer l.mutex.Unlock() 419 if l.limiter != nil { 420 l.limiter.SetBurst(burst) 421 } else { 422 l.limiter = rate.NewLimiter(l.params.RateLimit, burst) 423 } 424 } 425 426 func (l *APILimiter) delayedAdjustment(current, min, max float64) (n float64) { 427 n = current * l.adjustmentFactor 428 n = current + ((n - current) * l.params.DelayedAdjustmentFactor) 429 if min > 0.0 && n < min { 430 n = min 431 } 432 if max > 0.0 && n > max { 433 n = max 434 } 435 return 436 } 437 438 func (l *APILimiter) calculateAdjustmentFactor() float64 { 439 f := l.params.EstimatedProcessingDuration.Seconds() / l.meanProcessingDuration 440 if f > l.params.MaxAdjustmentFactor { 441 f = l.params.MaxAdjustmentFactor 442 } 443 if f < 1.0/l.params.MaxAdjustmentFactor { 444 f = 1.0 / l.params.MaxAdjustmentFactor 445 } 446 return f 447 } 448 449 func (l *APILimiter) adjustmentLimit(newValue, initialValue float64) float64 { 450 return math.Max(initialValue/l.params.MaxAdjustmentFactor, math.Min(initialValue*l.params.MaxAdjustmentFactor, newValue)) 451 } 452 453 func (l *APILimiter) adjustedBurst() int { 454 newBurst := l.delayedAdjustment(float64(l.params.RateBurst), float64(l.params.MinParallelRequests), 0.0) 455 return int(math.Round(l.adjustmentLimit(newBurst, float64(l.params.RateBurst)))) 456 } 457 458 func (l *APILimiter) adjustedLimit() rate.Limit { 459 newLimit := rate.Limit(float64(l.params.RateLimit) * l.adjustmentFactor) 460 return rate.Limit(l.adjustmentLimit(float64(newLimit), float64(l.params.RateLimit))) 461 } 462 463 func (l *APILimiter) adjustedParallelRequests() int { 464 newParallelRequests := l.delayedAdjustment(float64(l.params.ParallelRequests), 465 float64(l.params.MinParallelRequests), float64(l.params.MaxParallelRequests)) 466 return int(l.adjustmentLimit(newParallelRequests, float64(l.params.ParallelRequests))) 467 } 468 469 func (l *APILimiter) requestFinished(r *limitedRequest, err error, code int) { 470 if r.finished { 471 return 472 } 473 474 r.finished = true 475 476 var processingDuration time.Duration 477 if !r.startTime.IsZero() { 478 processingDuration = time.Since(r.startTime) 479 } 480 481 totalDuration := time.Since(r.scheduleTime) 482 483 scopedLog := log.WithFields(logrus.Fields{ 484 logAPICallName: l.name, 485 logUUID: r.uuid, 486 logProcessingDuration: processingDuration, 487 logTotalDuration: totalDuration, 488 logWaitDurationTotal: r.waitDuration, 489 }) 490 491 if err != nil { 492 scopedLog = scopedLog.WithError(err) 493 } 494 495 if l.params.Log { 496 scopedLog.Info("API call has been processed") 497 } else { 498 scopedLog.Debug("API call has been processed") 499 } 500 501 if r.waitSemaphoreWeight != 0 { 502 l.parallelWaitSemaphore.Release(r.waitSemaphoreWeight) 503 } 504 505 l.mutex.Lock() 506 507 if !r.startTime.IsZero() { 508 l.requestsProcessed++ 509 l.currentRequestsInFlight-- 510 } 511 512 // Only auto-adjust ratelimiter using metrics from successful API requests 513 if err == nil { 514 l.processingDurations = append(l.processingDurations, processingDuration) 515 if exceed := len(l.processingDurations) - l.params.MeanOver; exceed > 0 { 516 l.processingDurations = l.processingDurations[exceed:] 517 } 518 l.meanProcessingDuration = calcMeanDuration(l.processingDurations) 519 520 l.waitDurations = append(l.waitDurations, r.waitDuration) 521 if exceed := len(l.waitDurations) - l.params.MeanOver; exceed > 0 { 522 l.waitDurations = l.waitDurations[exceed:] 523 } 524 l.meanWaitDuration = calcMeanDuration(l.waitDurations) 525 526 if l.params.AutoAdjust && l.params.EstimatedProcessingDuration != 0 { 527 l.adjustmentFactor = l.calculateAdjustmentFactor() 528 l.parallelRequests = l.adjustedParallelRequests() 529 530 if l.limiter != nil { 531 l.limiter.SetLimit(l.adjustedLimit()) 532 533 newBurst := l.adjustedBurst() 534 l.limiter.SetBurst(newBurst) 535 } 536 } 537 } 538 539 values := MetricsValues{ 540 EstimatedProcessingDuration: l.params.EstimatedProcessingDuration.Seconds(), 541 WaitDuration: r.waitDuration, 542 MaxWaitDuration: l.params.MaxWaitDuration, 543 MinWaitDuration: l.params.MinWaitDuration, 544 MeanProcessingDuration: l.meanProcessingDuration, 545 MeanWaitDuration: l.meanWaitDuration, 546 ParallelRequests: l.parallelRequests, 547 CurrentRequestsInFlight: l.currentRequestsInFlight, 548 AdjustmentFactor: l.adjustmentFactor, 549 Error: err, 550 Outcome: string(r.outcome), 551 ReturnCode: code, 552 } 553 554 if l.limiter != nil { 555 values.Limit = l.limiter.Limit() 556 values.Burst = l.limiter.Burst() 557 } 558 l.mutex.Unlock() 559 560 if l.metrics != nil { 561 l.metrics.ProcessedRequest(l.name, values) 562 } 563 } 564 565 // calcMeanDuration returns the mean duration in seconds 566 func calcMeanDuration(durations []time.Duration) float64 { 567 total := 0.0 568 for _, t := range durations { 569 total += t.Seconds() 570 } 571 return total / float64(len(durations)) 572 } 573 574 // LimitedRequest represents a request that is being limited. It is returned 575 // by Wait() and the caller of Wait() is responsible to call Done() or Error() 576 // when the API call has been processed or resulted in an error. It is safe to 577 // call Error() and then Done(). It is not safe to call Done(), Error(), or 578 // WaitDuration() concurrently. 579 type LimitedRequest interface { 580 Done() 581 Error(err error, code int) 582 WaitDuration() time.Duration 583 } 584 585 type limitedRequest struct { 586 limiter *APILimiter 587 startTime time.Time 588 scheduleTime time.Time 589 waitDuration time.Duration 590 waitSemaphoreWeight int64 591 uuid string 592 finished bool 593 outcome outcome 594 } 595 596 // WaitDuration returns the duration the request had to wait 597 func (l *limitedRequest) WaitDuration() time.Duration { 598 return l.waitDuration 599 } 600 601 // Done must be called when the API request has been successfully processed 602 func (l *limitedRequest) Done() { 603 l.limiter.requestFinished(l, nil, outcomeSuccessCode) 604 } 605 606 // Error must be called when the API request resulted in an error 607 func (l *limitedRequest) Error(err error, code int) { 608 l.limiter.requestFinished(l, err, code) 609 } 610 611 // Wait blocks until the next API call is allowed to be processed. If the 612 // configured MaxWaitDuration is exceeded, an error is returned. On success, a 613 // LimitedRequest is returned on which Done() must be called when the API call 614 // has completed or Error() if an error occurred. 615 func (l *APILimiter) Wait(ctx context.Context) (LimitedRequest, error) { 616 req, err := l.wait(ctx) 617 if err != nil { 618 l.requestFinished(req, err, outcomeErrorCode) 619 return nil, err 620 } 621 return req, nil 622 } 623 624 // wait implements the API rate limiting delaying functionality. Every error 625 // message and corresponding log message are documented in 626 // Documentation/configuration/api-rate-limiting.rst. If any changes related to 627 // errors or log messages are made to this function, please update the 628 // aforementioned page as well. 629 func (l *APILimiter) wait(ctx context.Context) (req *limitedRequest, err error) { 630 var ( 631 limitWaitDuration time.Duration 632 r *rate.Reservation 633 ) 634 635 req = &limitedRequest{ 636 limiter: l, 637 scheduleTime: time.Now(), 638 uuid: uuid.New().String(), 639 } 640 641 l.mutex.Lock() 642 643 l.requestsScheduled++ 644 645 scopedLog := log.WithFields(logrus.Fields{ 646 logAPICallName: l.name, 647 logUUID: req.uuid, 648 logParallelRequests: l.parallelRequests, 649 }) 650 651 if l.params.MaxWaitDuration > 0 { 652 scopedLog = scopedLog.WithField(logMaxWaitDuration, l.params.MaxWaitDuration) 653 } 654 655 if l.params.MinWaitDuration > 0 { 656 scopedLog = scopedLog.WithField(logMinWaitDuration, l.params.MinWaitDuration) 657 } 658 659 select { 660 case <-ctx.Done(): 661 if l.params.Log { 662 scopedLog.Warning("Not processing API request due to cancelled context") 663 } 664 l.mutex.Unlock() 665 req.outcome = outcomeReqCancelled 666 err = fmt.Errorf("%w: %w", ErrWaitCancelled, ctx.Err()) 667 return 668 default: 669 } 670 671 skip := l.params.SkipInitial > 0 && l.requestsScheduled <= int64(l.params.SkipInitial) 672 if skip { 673 scopedLog = scopedLog.WithField(logSkipped, skip) 674 } 675 676 parallelRequests := l.parallelRequests 677 meanProcessingDuration := l.meanProcessingDuration 678 l.mutex.Unlock() 679 680 if l.params.Log { 681 scopedLog.Info("Processing API request with rate limiter") 682 } else { 683 scopedLog.Debug("Processing API request with rate limiter") 684 } 685 686 if skip { 687 goto skipRateLimiter 688 } 689 690 if parallelRequests > 0 { 691 waitCtx := ctx 692 if l.params.MaxWaitDuration > 0 { 693 ctx2, cancel := context.WithTimeout(ctx, l.params.MaxWaitDuration) 694 defer cancel() 695 waitCtx = ctx2 696 } 697 w := int64(waitSemaphoreResolution / parallelRequests) 698 err2 := l.parallelWaitSemaphore.Acquire(waitCtx, w) 699 if err2 != nil { 700 if l.params.Log { 701 scopedLog.WithError(err2).Warning("Not processing API request. Wait duration for maximum parallel requests exceeds maximum") 702 } 703 req.outcome = outcomeParallelMaxWait 704 err = fmt.Errorf("timed out while waiting to be served with %d parallel requests: %w", parallelRequests, err2) 705 return 706 } 707 req.waitSemaphoreWeight = w 708 } 709 req.waitDuration = time.Since(req.scheduleTime) 710 711 l.mutex.Lock() 712 if l.limiter != nil { 713 r = l.limiter.Reserve() 714 limitWaitDuration = r.Delay() 715 716 scopedLog = scopedLog.WithFields(logrus.Fields{ 717 logLimit: fmt.Sprintf("%.2f/s", l.limiter.Limit()), 718 logBurst: l.limiter.Burst(), 719 logWaitDurationLimit: limitWaitDuration, 720 logMaxWaitDurationLimiter: l.params.MaxWaitDuration - req.waitDuration, 721 }) 722 } 723 l.mutex.Unlock() 724 725 if l.params.MinWaitDuration > 0 && limitWaitDuration < l.params.MinWaitDuration { 726 limitWaitDuration = l.params.MinWaitDuration 727 } 728 729 if (l.params.MaxWaitDuration > 0 && (limitWaitDuration+req.waitDuration) > l.params.MaxWaitDuration) || limitWaitDuration == rate.InfDuration { 730 if l.params.Log { 731 scopedLog.Warning("Not processing API request. Wait duration exceeds maximum") 732 } 733 734 // The rate limiter should only consider a reservation valid if 735 // the request is actually processed. Cancellation of the 736 // reservation should happen before we sleep below. 737 if r != nil { 738 r.Cancel() 739 } 740 741 // Instead of returning immediately, pace the caller by 742 // sleeping for the mean processing duration. This helps 743 // against callers who disrespect 429 error codes and retry 744 // immediately. 745 if meanProcessingDuration > 0.0 { 746 time.Sleep(time.Duration(meanProcessingDuration * float64(time.Second))) 747 } 748 749 req.outcome = outcomeLimitMaxWait 750 err = fmt.Errorf("request would have to wait %v to be served (maximum wait duration: %v)", 751 limitWaitDuration, l.params.MaxWaitDuration-req.waitDuration) 752 return 753 } 754 755 if limitWaitDuration != 0 { 756 select { 757 case <-time.After(limitWaitDuration): 758 case <-ctx.Done(): 759 if l.params.Log { 760 scopedLog.Warning("Not processing API request due to cancelled context while waiting") 761 } 762 // The rate limiter should only consider a reservation 763 // valid if the request is actually processed. 764 if r != nil { 765 r.Cancel() 766 } 767 768 req.outcome = outcomeReqCancelled 769 err = fmt.Errorf("%w: %w", ErrWaitCancelled, ctx.Err()) 770 return 771 } 772 } 773 774 req.waitDuration = time.Since(req.scheduleTime) 775 776 skipRateLimiter: 777 778 l.mutex.Lock() 779 l.currentRequestsInFlight++ 780 l.mutex.Unlock() 781 782 scopedLog = scopedLog.WithField(logWaitDurationTotal, req.waitDuration) 783 784 if l.params.Log { 785 scopedLog.Info("API request released by rate limiter") 786 } else { 787 scopedLog.Debug("API request released by rate limiter") 788 } 789 790 req.startTime = time.Now() 791 return req, nil 792 793 } 794 795 func parseRate(r string) (rate.Limit, error) { 796 tokens := strings.SplitN(r, "/", 2) 797 if len(tokens) != 2 { 798 return 0, fmt.Errorf("not in the form number/interval") 799 } 800 801 f, err := strconv.ParseFloat(tokens[0], 64) 802 if err != nil { 803 return 0, fmt.Errorf("unable to parse float %q: %w", tokens[0], err) 804 } 805 806 // Reject rates such as 1/1 or 10/10 as it will default to nanoseconds 807 // which is likely unexpected to the user. Require an explicit suffix. 808 if _, err := strconv.ParseInt(string(tokens[1]), 10, 64); err == nil { 809 return 0, fmt.Errorf("interval %q must contain duration suffix", tokens[1]) 810 } 811 812 // If duration is provided as "m" or "s", convert it into "1m" or "1s" 813 if _, err := strconv.ParseInt(string(tokens[1][0]), 10, 64); err != nil { 814 tokens[1] = "1" + tokens[1] 815 } 816 817 d, err := time.ParseDuration(tokens[1]) 818 if err != nil { 819 return 0, fmt.Errorf("unable to parse duration %q: %w", tokens[1], err) 820 } 821 822 return rate.Limit(f / d.Seconds()), nil 823 } 824 825 // APILimiterSet is a set of APILimiter indexed by name 826 type APILimiterSet struct { 827 limiters map[string]*APILimiter 828 metrics MetricsObserver 829 } 830 831 // MetricsValues is the snapshot of relevant values to feed into the 832 // MetricsObserver 833 type MetricsValues struct { 834 WaitDuration time.Duration 835 MinWaitDuration time.Duration 836 MaxWaitDuration time.Duration 837 Outcome string 838 MeanProcessingDuration float64 839 MeanWaitDuration float64 840 EstimatedProcessingDuration float64 841 ParallelRequests int 842 Limit rate.Limit 843 Burst int 844 CurrentRequestsInFlight int 845 AdjustmentFactor float64 846 Error error 847 ReturnCode int 848 } 849 850 // MetricsObserver is the interface that must be implemented to extract metrics 851 type MetricsObserver interface { 852 // ProcessedRequest is invoked after invocation of an API call 853 ProcessedRequest(name string, values MetricsValues) 854 } 855 856 // NewAPILimiterSet creates a new APILimiterSet based on a set of rate limiting 857 // configurations and the default configuration. Any rate limiter that is 858 // configured in the config OR the defaults will be configured and made 859 // available via the Limiter(name) and Wait() function. 860 func NewAPILimiterSet(config map[string]string, defaults map[string]APILimiterParameters, metrics MetricsObserver) (*APILimiterSet, error) { 861 limiters := map[string]*APILimiter{} 862 863 for name, p := range defaults { 864 // Merge user config into defaults when provided 865 if userConfig, ok := config[name]; ok { 866 combinedParams, err := p.MergeUserConfig(userConfig) 867 if err != nil { 868 return nil, err 869 } 870 p = combinedParams 871 } 872 873 limiters[name] = NewAPILimiter(name, p, metrics) 874 } 875 876 for name, c := range config { 877 if _, ok := defaults[name]; !ok { 878 l, err := NewAPILimiterFromConfig(name, c, metrics) 879 if err != nil { 880 return nil, fmt.Errorf("unable to parse rate limiting configuration %s=%s: %w", name, c, err) 881 } 882 883 limiters[name] = l 884 } 885 } 886 887 return &APILimiterSet{ 888 limiters: limiters, 889 metrics: metrics, 890 }, nil 891 } 892 893 // Limiter returns the APILimiter with a given name 894 func (s *APILimiterSet) Limiter(name string) *APILimiter { 895 return s.limiters[name] 896 } 897 898 type dummyRequest struct{} 899 900 func (d dummyRequest) WaitDuration() time.Duration { return 0 } 901 func (d dummyRequest) Done() {} 902 func (d dummyRequest) Error(err error, code int) {} 903 904 // Wait invokes Wait() on the APILimiter with the given name. If the limiter 905 // does not exist, a dummy limiter is used which will not impose any 906 // restrictions. 907 func (s *APILimiterSet) Wait(ctx context.Context, name string) (LimitedRequest, error) { 908 l, ok := s.limiters[name] 909 if !ok { 910 return dummyRequest{}, nil 911 } 912 913 return l.Wait(ctx) 914 } 915 916 // parsePositiveInt parses value as an int. It returns an error if value cannot 917 // be parsed or is negative. 918 func parsePositiveInt(value string) (int, error) { 919 switch i64, err := strconv.ParseInt(value, 10, 64); { 920 case err != nil: 921 return 0, fmt.Errorf("unable to parse positive integer %q: %w", value, err) 922 case i64 < 0: 923 return 0, fmt.Errorf("unable to parse positive integer %q: negative value", value) 924 case i64 > math.MaxInt: 925 return 0, fmt.Errorf("unable to parse positive integer %q: overflow", value) 926 default: 927 return int(i64), nil 928 } 929 }