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  }