go.temporal.io/server@v1.23.0/common/quotas/priority_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 "sort" 31 "time" 32 ) 33 34 type ( 35 // PriorityRateLimiterImpl is a wrapper around the golang rate limiter 36 PriorityRateLimiterImpl struct { 37 requestPriorityFn RequestPriorityFn 38 priorityToRateLimiters map[int]RequestRateLimiter 39 40 // priority value 0 means highest priority 41 // sorted rate limiter from low priority value to high priority value 42 priorityToIndex map[int]int 43 rateLimiters []RequestRateLimiter 44 } 45 ) 46 47 var _ RequestRateLimiter = (*PriorityRateLimiterImpl)(nil) 48 49 // NewPriorityRateLimiter returns a new rate limiter that can handle dynamic 50 // configuration updates 51 func NewPriorityRateLimiter( 52 requestPriorityFn RequestPriorityFn, 53 priorityToRateLimiters map[int]RequestRateLimiter, 54 ) *PriorityRateLimiterImpl { 55 priorities := make([]int, 0, len(priorityToRateLimiters)) 56 for priority := range priorityToRateLimiters { 57 priorities = append(priorities, priority) 58 } 59 sort.Slice(priorities, func(i, j int) bool { 60 return priorities[i] < priorities[j] 61 }) 62 priorityToIndex := make(map[int]int, len(priorityToRateLimiters)) 63 rateLimiters := make([]RequestRateLimiter, 0, len(priorityToRateLimiters)) 64 for index, priority := range priorities { 65 priorityToIndex[priority] = index 66 rateLimiters = append(rateLimiters, priorityToRateLimiters[priority]) 67 } 68 69 return &PriorityRateLimiterImpl{ 70 requestPriorityFn: requestPriorityFn, 71 priorityToRateLimiters: priorityToRateLimiters, 72 73 priorityToIndex: priorityToIndex, 74 rateLimiters: rateLimiters, 75 } 76 } 77 78 func (p *PriorityRateLimiterImpl) Allow( 79 now time.Time, 80 request Request, 81 ) bool { 82 decidingRateLimiter, consumeRateLimiters := p.getRateLimiters(request) 83 84 allow := decidingRateLimiter.Allow(now, request) 85 if !allow { 86 return false 87 } 88 89 for _, limiter := range consumeRateLimiters { 90 _ = limiter.Reserve(now, request) 91 } 92 return allow 93 } 94 95 func (p *PriorityRateLimiterImpl) Reserve( 96 now time.Time, 97 request Request, 98 ) Reservation { 99 decidingRateLimiter, consumeRateLimiters := p.getRateLimiters(request) 100 101 decidingReservation := decidingRateLimiter.Reserve(now, request) 102 if !decidingReservation.OK() { 103 return decidingReservation 104 } 105 106 otherReservations := make([]Reservation, len(consumeRateLimiters)) 107 for index, limiter := range consumeRateLimiters { 108 otherReservations[index] = limiter.Reserve(now, request) 109 } 110 return NewPriorityReservation(decidingReservation, otherReservations) 111 } 112 113 func (p *PriorityRateLimiterImpl) Wait( 114 ctx context.Context, 115 request Request, 116 ) error { 117 select { 118 case <-ctx.Done(): 119 return ctx.Err() 120 default: 121 } 122 123 now := time.Now().UTC() 124 reservation := p.Reserve(now, request) 125 if !reservation.OK() { 126 return fmt.Errorf("rate: Wait(n=%d) would exceed context deadline", request.Token) 127 } 128 129 delay := reservation.DelayFrom(now) 130 if delay == 0 { 131 return nil 132 } 133 waitLimit := InfDuration 134 if deadline, ok := ctx.Deadline(); ok { 135 waitLimit = deadline.Sub(now) 136 } 137 if waitLimit < delay { 138 reservation.CancelAt(now) 139 return fmt.Errorf("rate: Wait(n=%d) would exceed context deadline", request.Token) 140 } 141 142 t := time.NewTimer(delay) 143 defer t.Stop() 144 select { 145 case <-t.C: 146 return nil 147 148 case <-ctx.Done(): 149 reservation.CancelAt(time.Now()) 150 return ctx.Err() 151 } 152 } 153 154 func (p *PriorityRateLimiterImpl) getRateLimiters( 155 request Request, 156 ) (RequestRateLimiter, []RequestRateLimiter) { 157 priority := p.requestPriorityFn(request) 158 if _, ok := p.priorityToRateLimiters[priority]; !ok { 159 panic("Request to priority & priority to rate limiter does not match") 160 } 161 162 rateLimiterIndex := p.priorityToIndex[priority] 163 return p.rateLimiters[rateLimiterIndex], p.rateLimiters[rateLimiterIndex+1:] 164 }