go.temporal.io/server@v1.23.0/common/quotas/priority_rate_limiter_impl_test.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  	"testing"
    30  	"time"
    31  
    32  	"github.com/golang/mock/gomock"
    33  	"github.com/stretchr/testify/require"
    34  	"github.com/stretchr/testify/suite"
    35  )
    36  
    37  type (
    38  	priorityStageRateLimiterSuite struct {
    39  		suite.Suite
    40  		*require.Assertions
    41  
    42  		controller              *gomock.Controller
    43  		highPriorityRateLimiter *MockRateLimiter
    44  		lowPriorityRateLimiter  *MockRateLimiter
    45  		highPriorityReservation *MockReservation
    46  		lowPriorityReservation  *MockReservation
    47  		highPriorityAPIName     string
    48  		lowPriorityAPIName      string
    49  
    50  		rateLimiter *PriorityRateLimiterImpl
    51  	}
    52  )
    53  
    54  func TestPriorityStageRateLimiterSuite(t *testing.T) {
    55  	s := new(priorityStageRateLimiterSuite)
    56  	suite.Run(t, s)
    57  }
    58  
    59  func (s *priorityStageRateLimiterSuite) SetupSuite() {
    60  
    61  }
    62  
    63  func (s *priorityStageRateLimiterSuite) TearDownSuite() {
    64  
    65  }
    66  
    67  func (s *priorityStageRateLimiterSuite) SetupTest() {
    68  	s.Assertions = require.New(s.T())
    69  
    70  	s.controller = gomock.NewController(s.T())
    71  	s.highPriorityRateLimiter = NewMockRateLimiter(s.controller)
    72  	s.lowPriorityRateLimiter = NewMockRateLimiter(s.controller)
    73  	s.highPriorityReservation = NewMockReservation(s.controller)
    74  	s.lowPriorityReservation = NewMockReservation(s.controller)
    75  
    76  	s.highPriorityAPIName = "high-priority"
    77  	s.lowPriorityAPIName = "low-priority"
    78  	apiToPriority := map[string]int{
    79  		s.highPriorityAPIName: 0,
    80  		s.lowPriorityAPIName:  2,
    81  	}
    82  	priorityToRateLimiters := map[int]RequestRateLimiter{
    83  		0: NewRequestRateLimiterAdapter(s.highPriorityRateLimiter),
    84  		2: NewRequestRateLimiterAdapter(s.lowPriorityRateLimiter),
    85  	}
    86  	s.rateLimiter = NewPriorityRateLimiter(func(req Request) int {
    87  		return apiToPriority[req.API]
    88  	}, priorityToRateLimiters)
    89  
    90  }
    91  
    92  func (s *priorityStageRateLimiterSuite) TearDownTest() {
    93  	s.controller.Finish()
    94  }
    95  
    96  func (s *priorityStageRateLimiterSuite) TestAllow_HighPriority_Allow() {
    97  	now := time.Now()
    98  	token := 1
    99  	req := Request{
   100  		API:    s.highPriorityAPIName,
   101  		Token:  token,
   102  		Caller: "",
   103  	}
   104  
   105  	s.highPriorityRateLimiter.EXPECT().AllowN(now, token).Return(true)
   106  	s.lowPriorityRateLimiter.EXPECT().ReserveN(now, token).Return(s.lowPriorityReservation)
   107  
   108  	allow := s.rateLimiter.Allow(now, req)
   109  	s.True(allow)
   110  }
   111  
   112  func (s *priorityStageRateLimiterSuite) TestAllow_HighPriority_Disallow() {
   113  	now := time.Now()
   114  	token := 1
   115  	req := Request{
   116  		API:    s.highPriorityAPIName,
   117  		Token:  token,
   118  		Caller: "",
   119  	}
   120  
   121  	s.highPriorityRateLimiter.EXPECT().AllowN(now, token).Return(false)
   122  
   123  	allow := s.rateLimiter.Allow(now, req)
   124  	s.False(allow)
   125  }
   126  
   127  func (s *priorityStageRateLimiterSuite) TestAllow_LowPriority_Allow() {
   128  	now := time.Now()
   129  	token := 1
   130  	req := Request{
   131  		API:    s.lowPriorityAPIName,
   132  		Token:  token,
   133  		Caller: "",
   134  	}
   135  
   136  	s.lowPriorityRateLimiter.EXPECT().AllowN(now, token).Return(true)
   137  
   138  	allow := s.rateLimiter.Allow(now, req)
   139  	s.True(allow)
   140  }
   141  
   142  func (s *priorityStageRateLimiterSuite) TestAllow_LowPriority_Disallow() {
   143  	now := time.Now()
   144  	token := 1
   145  	req := Request{
   146  		API:    s.lowPriorityAPIName,
   147  		Token:  token,
   148  		Caller: "",
   149  	}
   150  
   151  	s.lowPriorityRateLimiter.EXPECT().AllowN(now, token).Return(false)
   152  
   153  	allow := s.rateLimiter.Allow(now, req)
   154  	s.False(allow)
   155  }
   156  
   157  func (s *priorityStageRateLimiterSuite) TestReserve_HighPriority_OK() {
   158  	now := time.Now()
   159  	token := 1
   160  	req := Request{
   161  		API:    s.highPriorityAPIName,
   162  		Token:  token,
   163  		Caller: "",
   164  	}
   165  
   166  	s.highPriorityReservation.EXPECT().OK().Return(true)
   167  	s.highPriorityRateLimiter.EXPECT().ReserveN(now, token).Return(s.highPriorityReservation)
   168  	s.lowPriorityRateLimiter.EXPECT().ReserveN(now, token).Return(s.lowPriorityReservation)
   169  
   170  	reservation := s.rateLimiter.Reserve(now, req)
   171  	s.Equal(NewPriorityReservation(
   172  		s.highPriorityReservation,
   173  		[]Reservation{s.lowPriorityReservation},
   174  	), reservation)
   175  }
   176  
   177  func (s *priorityStageRateLimiterSuite) TestReserve_HighPriority_NotOK() {
   178  	now := time.Now()
   179  	token := 1
   180  	req := Request{
   181  		API:    s.highPriorityAPIName,
   182  		Token:  token,
   183  		Caller: "",
   184  	}
   185  
   186  	s.highPriorityReservation.EXPECT().OK().Return(false)
   187  	s.highPriorityRateLimiter.EXPECT().ReserveN(now, token).Return(s.highPriorityReservation)
   188  
   189  	reservation := s.rateLimiter.Reserve(now, req)
   190  	s.Equal(s.highPriorityReservation, reservation)
   191  }
   192  
   193  func (s *priorityStageRateLimiterSuite) TestReserve_LowPriority_OK() {
   194  	now := time.Now()
   195  	token := 1
   196  	req := Request{
   197  		API:    s.lowPriorityAPIName,
   198  		Token:  token,
   199  		Caller: "",
   200  	}
   201  
   202  	s.lowPriorityReservation.EXPECT().OK().Return(true)
   203  	s.lowPriorityRateLimiter.EXPECT().ReserveN(now, token).Return(s.lowPriorityReservation)
   204  
   205  	reservation := s.rateLimiter.Reserve(now, req)
   206  	s.Equal(NewPriorityReservation(
   207  		s.lowPriorityReservation,
   208  		[]Reservation{},
   209  	), reservation)
   210  }
   211  
   212  func (s *priorityStageRateLimiterSuite) TestReserve_LowPriority_NotOK() {
   213  	now := time.Now()
   214  	token := 1
   215  	req := Request{
   216  		API:    s.lowPriorityAPIName,
   217  		Token:  token,
   218  		Caller: "",
   219  	}
   220  
   221  	s.lowPriorityReservation.EXPECT().OK().Return(false)
   222  	s.lowPriorityRateLimiter.EXPECT().ReserveN(now, token).Return(s.lowPriorityReservation)
   223  
   224  	reservation := s.rateLimiter.Reserve(now, req)
   225  	s.Equal(s.lowPriorityReservation, reservation)
   226  }
   227  
   228  func (s *priorityStageRateLimiterSuite) TestWait_HighPriority_AlreadyExpired() {
   229  	ctx, cancel := context.WithCancel(context.Background())
   230  	cancel()
   231  	token := 1
   232  	req := Request{
   233  		API:    s.highPriorityAPIName,
   234  		Token:  token,
   235  		Caller: "",
   236  	}
   237  
   238  	err := s.rateLimiter.Wait(ctx, req)
   239  	s.Error(err)
   240  }
   241  
   242  func (s *priorityStageRateLimiterSuite) TestWait_LowPriority_AlreadyExpired() {
   243  	ctx, cancel := context.WithCancel(context.Background())
   244  	cancel()
   245  	token := 1
   246  	req := Request{
   247  		API:    s.lowPriorityAPIName,
   248  		Token:  token,
   249  		Caller: "",
   250  	}
   251  
   252  	err := s.rateLimiter.Wait(ctx, req)
   253  	s.Error(err)
   254  }
   255  
   256  func (s *priorityStageRateLimiterSuite) TestWait_HighPriority_NotExpired_WithExpiration_Error() {
   257  	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   258  	defer cancel()
   259  	token := 1
   260  	req := Request{
   261  		API:    s.highPriorityAPIName,
   262  		Token:  token,
   263  		Caller: "",
   264  	}
   265  
   266  	highPriorityReservationDelay := 2 * time.Second
   267  	s.highPriorityReservation.EXPECT().DelayFrom(gomock.Any()).Return(highPriorityReservationDelay)
   268  	s.highPriorityReservation.EXPECT().CancelAt(gomock.Any())
   269  	s.lowPriorityReservation.EXPECT().CancelAt(gomock.Any())
   270  
   271  	s.highPriorityReservation.EXPECT().OK().Return(true).AnyTimes()
   272  	s.highPriorityRateLimiter.EXPECT().ReserveN(gomock.Any(), token).Return(s.highPriorityReservation)
   273  	s.lowPriorityReservation.EXPECT().OK().Return(true).AnyTimes()
   274  	s.lowPriorityRateLimiter.EXPECT().ReserveN(gomock.Any(), token).Return(s.lowPriorityReservation)
   275  
   276  	err := s.rateLimiter.Wait(ctx, req)
   277  	s.Error(err)
   278  }
   279  
   280  func (s *priorityStageRateLimiterSuite) TestWait_LowPriority_NotExpired_WithExpiration_Error() {
   281  	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   282  	defer cancel()
   283  	token := 1
   284  	req := Request{
   285  		API:    s.lowPriorityAPIName,
   286  		Token:  token,
   287  		Caller: "",
   288  	}
   289  
   290  	lowPriorityReservationDelay := 3 * time.Second
   291  	s.lowPriorityReservation.EXPECT().DelayFrom(gomock.Any()).Return(lowPriorityReservationDelay)
   292  	s.lowPriorityReservation.EXPECT().CancelAt(gomock.Any())
   293  
   294  	s.lowPriorityReservation.EXPECT().OK().Return(true).AnyTimes()
   295  	s.lowPriorityRateLimiter.EXPECT().ReserveN(gomock.Any(), token).Return(s.lowPriorityReservation)
   296  
   297  	err := s.rateLimiter.Wait(ctx, req)
   298  	s.Error(err)
   299  }
   300  
   301  func (s *priorityStageRateLimiterSuite) TestWait_HighPriority_NotExpired_WithExpiration_Cancelled() {
   302  	ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second)
   303  	token := 1
   304  	req := Request{
   305  		API:    s.highPriorityAPIName,
   306  		Token:  token,
   307  		Caller: "",
   308  	}
   309  
   310  	go func() {
   311  		time.Sleep(4 * time.Second)
   312  		cancel()
   313  	}()
   314  
   315  	highPriorityReservationDelay := 20 * time.Second
   316  	s.highPriorityReservation.EXPECT().DelayFrom(gomock.Any()).Return(highPriorityReservationDelay)
   317  	s.highPriorityReservation.EXPECT().CancelAt(gomock.Any())
   318  	s.lowPriorityReservation.EXPECT().CancelAt(gomock.Any())
   319  
   320  	s.highPriorityReservation.EXPECT().OK().Return(true).AnyTimes()
   321  	s.highPriorityRateLimiter.EXPECT().ReserveN(gomock.Any(), token).Return(s.highPriorityReservation)
   322  	s.lowPriorityReservation.EXPECT().OK().Return(true).AnyTimes()
   323  	s.lowPriorityRateLimiter.EXPECT().ReserveN(gomock.Any(), token).Return(s.lowPriorityReservation)
   324  
   325  	err := s.rateLimiter.Wait(ctx, req)
   326  	s.Error(err)
   327  }
   328  
   329  func (s *priorityStageRateLimiterSuite) TestWait_LowPriority_NotExpired_WithExpiration_Cancelled() {
   330  	ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second)
   331  	token := 1
   332  	req := Request{
   333  		API:    s.lowPriorityAPIName,
   334  		Token:  token,
   335  		Caller: "",
   336  	}
   337  
   338  	go func() {
   339  		time.Sleep(4 * time.Second)
   340  		cancel()
   341  	}()
   342  
   343  	lowPriorityReservationDelay := 30 * time.Second
   344  	s.lowPriorityReservation.EXPECT().DelayFrom(gomock.Any()).Return(lowPriorityReservationDelay)
   345  	s.lowPriorityReservation.EXPECT().CancelAt(gomock.Any())
   346  
   347  	s.lowPriorityReservation.EXPECT().OK().Return(true).AnyTimes()
   348  	s.lowPriorityRateLimiter.EXPECT().ReserveN(gomock.Any(), token).Return(s.lowPriorityReservation)
   349  
   350  	err := s.rateLimiter.Wait(ctx, req)
   351  	s.Error(err)
   352  }
   353  
   354  func (s *priorityStageRateLimiterSuite) TestWait_HighPriority_NotExpired_WithExpiration_NoError() {
   355  	ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second)
   356  	defer cancel()
   357  	token := 1
   358  	req := Request{
   359  		API:    s.highPriorityAPIName,
   360  		Token:  token,
   361  		Caller: "",
   362  	}
   363  
   364  	highPriorityReservationDelay := 2 * time.Second
   365  	s.highPriorityReservation.EXPECT().DelayFrom(gomock.Any()).Return(highPriorityReservationDelay)
   366  
   367  	s.highPriorityReservation.EXPECT().OK().Return(true).AnyTimes()
   368  	s.highPriorityRateLimiter.EXPECT().ReserveN(gomock.Any(), token).Return(s.highPriorityReservation)
   369  	s.lowPriorityReservation.EXPECT().OK().Return(true).AnyTimes()
   370  	s.lowPriorityRateLimiter.EXPECT().ReserveN(gomock.Any(), token).Return(s.lowPriorityReservation)
   371  
   372  	err := s.rateLimiter.Wait(ctx, req)
   373  	s.NoError(err)
   374  }
   375  
   376  func (s *priorityStageRateLimiterSuite) TestWait_LowPriority_NotExpired_WithExpiration_NoError() {
   377  	ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second)
   378  	defer cancel()
   379  	token := 1
   380  	req := Request{
   381  		API:    s.lowPriorityAPIName,
   382  		Token:  token,
   383  		Caller: "",
   384  	}
   385  
   386  	lowPriorityReservationDelay := 2 * time.Second
   387  	s.lowPriorityReservation.EXPECT().DelayFrom(gomock.Any()).Return(lowPriorityReservationDelay)
   388  
   389  	s.lowPriorityReservation.EXPECT().OK().Return(true).AnyTimes()
   390  	s.lowPriorityRateLimiter.EXPECT().ReserveN(gomock.Any(), token).Return(s.lowPriorityReservation)
   391  
   392  	err := s.rateLimiter.Wait(ctx, req)
   393  	s.NoError(err)
   394  }
   395  
   396  func (s *priorityStageRateLimiterSuite) TestWait_HighPriority_NotExpired_WithoutExpiration() {
   397  	ctx := context.Background()
   398  	token := 1
   399  	req := Request{
   400  		API:    s.highPriorityAPIName,
   401  		Token:  token,
   402  		Caller: "",
   403  	}
   404  
   405  	highPriorityReservationDelay := 2 * time.Second
   406  	s.highPriorityReservation.EXPECT().DelayFrom(gomock.Any()).Return(highPriorityReservationDelay)
   407  
   408  	s.highPriorityReservation.EXPECT().OK().Return(true).AnyTimes()
   409  	s.highPriorityRateLimiter.EXPECT().ReserveN(gomock.Any(), token).Return(s.highPriorityReservation)
   410  	s.lowPriorityReservation.EXPECT().OK().Return(true).AnyTimes()
   411  	s.lowPriorityRateLimiter.EXPECT().ReserveN(gomock.Any(), token).Return(s.lowPriorityReservation)
   412  
   413  	err := s.rateLimiter.Wait(ctx, req)
   414  	s.NoError(err)
   415  }
   416  
   417  func (s *priorityStageRateLimiterSuite) TestWait_LowPriority_NotExpired_WithoutExpiration() {
   418  	ctx := context.Background()
   419  	token := 1
   420  	req := Request{
   421  		API:    s.lowPriorityAPIName,
   422  		Token:  token,
   423  		Caller: "",
   424  	}
   425  
   426  	lowPriorityReservationDelay := 3 * time.Second
   427  	s.lowPriorityReservation.EXPECT().DelayFrom(gomock.Any()).Return(lowPriorityReservationDelay)
   428  
   429  	s.lowPriorityReservation.EXPECT().OK().Return(true).AnyTimes()
   430  	s.lowPriorityRateLimiter.EXPECT().ReserveN(gomock.Any(), token).Return(s.lowPriorityReservation)
   431  
   432  	err := s.rateLimiter.Wait(ctx, req)
   433  	s.NoError(err)
   434  }