github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/ingester/limiter_test.go (about)

     1  package ingester
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/stretchr/testify/assert"
    10  	"github.com/stretchr/testify/require"
    11  	"golang.org/x/time/rate"
    12  
    13  	"github.com/grafana/loki/pkg/validation"
    14  )
    15  
    16  func TestLimiter_AssertMaxStreamsPerUser(t *testing.T) {
    17  	tests := map[string]struct {
    18  		maxLocalStreamsPerUser  int
    19  		maxGlobalStreamsPerUser int
    20  		ringReplicationFactor   int
    21  		ringIngesterCount       int
    22  		streams                 int
    23  		expected                error
    24  	}{
    25  		"both local and global limit are disabled": {
    26  			maxLocalStreamsPerUser:  0,
    27  			maxGlobalStreamsPerUser: 0,
    28  			ringReplicationFactor:   1,
    29  			ringIngesterCount:       1,
    30  			streams:                 100,
    31  			expected:                nil,
    32  		},
    33  		"current number of streams is below the limit": {
    34  			maxLocalStreamsPerUser:  0,
    35  			maxGlobalStreamsPerUser: 1000,
    36  			ringReplicationFactor:   3,
    37  			ringIngesterCount:       10,
    38  			streams:                 299,
    39  			expected:                nil,
    40  		},
    41  		"current number of streams is above the limit": {
    42  			maxLocalStreamsPerUser:  0,
    43  			maxGlobalStreamsPerUser: 1000,
    44  			ringReplicationFactor:   3,
    45  			ringIngesterCount:       10,
    46  			streams:                 300,
    47  			expected:                fmt.Errorf(errMaxStreamsPerUserLimitExceeded, "test", 300, 300, 0, 1000, 300),
    48  		},
    49  		"both local and global limits are disabled": {
    50  			maxLocalStreamsPerUser:  0,
    51  			maxGlobalStreamsPerUser: 0,
    52  			ringReplicationFactor:   1,
    53  			ringIngesterCount:       1,
    54  			streams:                 math.MaxInt32 - 1,
    55  			expected:                nil,
    56  		},
    57  		"only local limit is enabled": {
    58  			maxLocalStreamsPerUser:  1000,
    59  			maxGlobalStreamsPerUser: 0,
    60  			ringReplicationFactor:   1,
    61  			ringIngesterCount:       1,
    62  			streams:                 3000,
    63  			expected:                fmt.Errorf(errMaxStreamsPerUserLimitExceeded, "test", 3000, 1000, 1000, 0, 0),
    64  		},
    65  		"only global limit is enabled with replication-factor=1": {
    66  			maxLocalStreamsPerUser:  0,
    67  			maxGlobalStreamsPerUser: 1000,
    68  			ringReplicationFactor:   1,
    69  			ringIngesterCount:       10,
    70  			streams:                 3000,
    71  			expected:                fmt.Errorf(errMaxStreamsPerUserLimitExceeded, "test", 3000, 100, 0, 1000, 100),
    72  		},
    73  		"only global limit is enabled with replication-factor=3": {
    74  			maxLocalStreamsPerUser:  0,
    75  			maxGlobalStreamsPerUser: 1000,
    76  			ringReplicationFactor:   3,
    77  			ringIngesterCount:       10,
    78  			streams:                 3000,
    79  			expected:                fmt.Errorf(errMaxStreamsPerUserLimitExceeded, "test", 3000, 300, 0, 1000, 300),
    80  		},
    81  		"both local and global limits are set with local limit < global limit": {
    82  			maxLocalStreamsPerUser:  150,
    83  			maxGlobalStreamsPerUser: 1000,
    84  			ringReplicationFactor:   3,
    85  			ringIngesterCount:       10,
    86  			streams:                 3000,
    87  			expected:                fmt.Errorf(errMaxStreamsPerUserLimitExceeded, "test", 3000, 150, 150, 1000, 300),
    88  		},
    89  		"both local and global limits are set with local limit > global limit": {
    90  			maxLocalStreamsPerUser:  500,
    91  			maxGlobalStreamsPerUser: 1000,
    92  			ringReplicationFactor:   3,
    93  			ringIngesterCount:       10,
    94  			streams:                 3000,
    95  			expected:                fmt.Errorf(errMaxStreamsPerUserLimitExceeded, "test", 3000, 300, 500, 1000, 300),
    96  		},
    97  	}
    98  
    99  	for testName, testData := range tests {
   100  		testData := testData
   101  
   102  		t.Run(testName, func(t *testing.T) {
   103  			// Mock the ring
   104  			ring := &ringCountMock{count: testData.ringIngesterCount}
   105  
   106  			// Mock limits
   107  			limits, err := validation.NewOverrides(validation.Limits{
   108  				MaxLocalStreamsPerUser:  testData.maxLocalStreamsPerUser,
   109  				MaxGlobalStreamsPerUser: testData.maxGlobalStreamsPerUser,
   110  			}, nil)
   111  			require.NoError(t, err)
   112  
   113  			limiter := NewLimiter(limits, NilMetrics, ring, testData.ringReplicationFactor)
   114  			actual := limiter.AssertMaxStreamsPerUser("test", testData.streams)
   115  
   116  			assert.Equal(t, testData.expected, actual)
   117  		})
   118  	}
   119  }
   120  
   121  func TestLimiter_minNonZero(t *testing.T) {
   122  	t.Parallel()
   123  
   124  	tests := map[string]struct {
   125  		first    int
   126  		second   int
   127  		expected int
   128  	}{
   129  		"both zero": {
   130  			first:    0,
   131  			second:   0,
   132  			expected: 0,
   133  		},
   134  		"first is zero": {
   135  			first:    0,
   136  			second:   1,
   137  			expected: 1,
   138  		},
   139  		"second is zero": {
   140  			first:    1,
   141  			second:   0,
   142  			expected: 1,
   143  		},
   144  		"both non zero, second > first": {
   145  			first:    1,
   146  			second:   2,
   147  			expected: 1,
   148  		},
   149  		"both non zero, first > second": {
   150  			first:    2,
   151  			second:   1,
   152  			expected: 1,
   153  		},
   154  	}
   155  
   156  	for testName, testData := range tests {
   157  		testData := testData
   158  
   159  		t.Run(testName, func(t *testing.T) {
   160  			limiter := NewLimiter(nil, NilMetrics, nil, 0)
   161  			assert.Equal(t, testData.expected, limiter.minNonZero(testData.first, testData.second))
   162  		})
   163  	}
   164  }
   165  
   166  type ringCountMock struct {
   167  	count int
   168  }
   169  
   170  func (m *ringCountMock) HealthyInstancesCount() int {
   171  	return m.count
   172  }
   173  
   174  // Assert some of the weirder (bug?) behavior of golang.org/x/time/rate
   175  func TestGoLimiter(t *testing.T) {
   176  	for _, tc := range []struct {
   177  		desc  string
   178  		lim   *rate.Limiter
   179  		at    time.Time
   180  		burst int
   181  		limit rate.Limit
   182  		allow int
   183  		exp   bool
   184  	}{
   185  		{
   186  			// I (owen-d) think this _should_ work and am supplying this test
   187  			// case by way of explanation for how the StreamRateLimiter
   188  			// works around the rate.Inf edge case.
   189  			desc:  "changing inf limits unnecessarily cordons",
   190  			lim:   rate.NewLimiter(rate.Inf, 0),
   191  			at:    time.Now(),
   192  			burst: 2,
   193  			limit: 1,
   194  			allow: 1,
   195  			exp:   false,
   196  		},
   197  		{
   198  			desc:  "non inf limit works",
   199  			lim:   rate.NewLimiter(1, 2),
   200  			at:    time.Now(),
   201  			burst: 2,
   202  			limit: 1,
   203  			allow: 1,
   204  			exp:   true,
   205  		},
   206  	} {
   207  		t.Run(tc.desc, func(t *testing.T) {
   208  			tc.lim.SetBurstAt(tc.at, tc.burst)
   209  			tc.lim.SetLimitAt(tc.at, tc.limit)
   210  			require.Equal(t, tc.exp, tc.lim.AllowN(tc.at, tc.allow))
   211  		})
   212  	}
   213  }