go.temporal.io/server@v1.23.0/common/quotas/multi_request_rate_limiter_impl.go (about) 1 // The MIT License 2 // 3 // Copyright (c) 2020 Temporal Technologies Inc. All rights reserved. 4 // 5 // Copyright (c) 2020 Uber Technologies, Inc. 6 // 7 // Permission is hereby granted, free of charge, to any person obtaining a copy 8 // of this software and associated documentation files (the "Software"), to deal 9 // in the Software without restriction, including without limitation the rights 10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 // copies of the Software, and to permit persons to whom the Software is 12 // furnished to do so, subject to the following conditions: 13 // 14 // The above copyright notice and this permission notice shall be included in 15 // all copies or substantial portions of the Software. 16 // 17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 // THE SOFTWARE. 24 25 package quotas 26 27 import ( 28 "context" 29 "fmt" 30 "time" 31 ) 32 33 var _ RequestRateLimiter = (*MultiRequestRateLimiterImpl)(nil) 34 35 type ( 36 MultiRequestRateLimiterImpl struct { 37 requestRateLimiters []RequestRateLimiter 38 } 39 ) 40 41 func NewMultiRequestRateLimiter( 42 requestRateLimiters ...RequestRateLimiter, 43 ) *MultiRequestRateLimiterImpl { 44 if len(requestRateLimiters) == 0 { 45 panic("expect at least one rate limiter") 46 } 47 return &MultiRequestRateLimiterImpl{ 48 requestRateLimiters: requestRateLimiters, 49 } 50 } 51 52 func (rl *MultiRequestRateLimiterImpl) Allow(now time.Time, request Request) bool { 53 length := len(rl.requestRateLimiters) 54 reservations := make([]Reservation, 0, length) 55 56 for _, requestRateLimiter := range rl.requestRateLimiters { 57 reservation := requestRateLimiter.Reserve(now, request) 58 if !reservation.OK() || reservation.DelayFrom(now) > 0 { 59 if reservation.OK() { 60 reservation.CancelAt(now) 61 } 62 63 // cancel all existing reservation 64 for _, reservation := range reservations { 65 reservation.CancelAt(now) 66 } 67 return false 68 } 69 reservations = append(reservations, reservation) 70 } 71 72 return true 73 } 74 75 func (rl *MultiRequestRateLimiterImpl) Reserve(now time.Time, request Request) Reservation { 76 length := len(rl.requestRateLimiters) 77 reservations := make([]Reservation, 0, length) 78 79 for _, requestRateLimiter := range rl.requestRateLimiters { 80 reservation := requestRateLimiter.Reserve(now, request) 81 if !reservation.OK() { 82 // cancel all existing reservation 83 for _, reservation := range reservations { 84 reservation.CancelAt(now) 85 } 86 return NewMultiReservation(false, nil) 87 } 88 reservations = append(reservations, reservation) 89 } 90 91 return NewMultiReservation(true, reservations) 92 } 93 94 func (rl *MultiRequestRateLimiterImpl) Wait(ctx context.Context, request Request) error { 95 select { 96 case <-ctx.Done(): 97 return ctx.Err() 98 default: 99 } 100 101 now := time.Now().UTC() 102 reservation := rl.Reserve(now, request) 103 if !reservation.OK() { 104 return fmt.Errorf("rate: Wait(n=%d) would exceed context deadline", request.Token) 105 } 106 107 delay := reservation.DelayFrom(now) 108 if delay == 0 { 109 return nil 110 } 111 waitLimit := InfDuration 112 if deadline, ok := ctx.Deadline(); ok { 113 waitLimit = deadline.Sub(now) 114 } 115 if waitLimit < delay { 116 reservation.CancelAt(now) 117 return fmt.Errorf("rate: Wait(n=%d) would exceed context deadline", request.Token) 118 } 119 120 t := time.NewTimer(delay) 121 defer t.Stop() 122 select { 123 case <-t.C: 124 return nil 125 126 case <-ctx.Done(): 127 reservation.CancelAt(time.Now()) 128 return ctx.Err() 129 } 130 }