github.com/muhammadn/cortex@v1.9.1-0.20220510110439-46bb7000d03d/pkg/ingester/user_state_test.go (about) 1 package ingester 2 3 import ( 4 "context" 5 "fmt" 6 "math" 7 "strings" 8 "testing" 9 "time" 10 11 "github.com/prometheus/client_golang/prometheus" 12 "github.com/prometheus/client_golang/prometheus/testutil" 13 "github.com/prometheus/common/model" 14 "github.com/prometheus/prometheus/pkg/labels" 15 "github.com/stretchr/testify/assert" 16 "github.com/stretchr/testify/require" 17 "github.com/weaveworks/common/user" 18 19 "github.com/cortexproject/cortex/pkg/util" 20 "github.com/cortexproject/cortex/pkg/util/validation" 21 ) 22 23 // Test forSeriesMatching correctly batches up series. 24 func TestForSeriesMatchingBatching(t *testing.T) { 25 matchAllNames, err := labels.NewMatcher(labels.MatchRegexp, model.MetricNameLabel, ".+") 26 require.NoError(t, err) 27 // We rely on pushTestSamples() creating jobs "testjob0" and "testjob1" in equal parts 28 matchNotJob0, err := labels.NewMatcher(labels.MatchNotEqual, model.JobLabel, "testjob0") 29 require.NoError(t, err) 30 matchNotJob1, err := labels.NewMatcher(labels.MatchNotEqual, model.JobLabel, "testjob1") 31 require.NoError(t, err) 32 33 for _, tc := range []struct { 34 numSeries, batchSize int 35 matchers []*labels.Matcher 36 expected int 37 }{ 38 {100, 10, []*labels.Matcher{matchAllNames}, 100}, 39 {99, 10, []*labels.Matcher{matchAllNames}, 99}, 40 {98, 10, []*labels.Matcher{matchAllNames}, 98}, 41 {5, 10, []*labels.Matcher{matchAllNames}, 5}, 42 {10, 1, []*labels.Matcher{matchAllNames}, 10}, 43 {1, 1, []*labels.Matcher{matchAllNames}, 1}, 44 {10, 10, []*labels.Matcher{matchAllNames, matchNotJob0}, 5}, 45 {10, 10, []*labels.Matcher{matchAllNames, matchNotJob1}, 5}, 46 {100, 10, []*labels.Matcher{matchAllNames, matchNotJob0}, 50}, 47 {100, 10, []*labels.Matcher{matchAllNames, matchNotJob1}, 50}, 48 {99, 10, []*labels.Matcher{matchAllNames, matchNotJob0}, 49}, 49 {99, 10, []*labels.Matcher{matchAllNames, matchNotJob1}, 50}, 50 } { 51 t.Run(fmt.Sprintf("numSeries=%d,batchSize=%d,matchers=%s", tc.numSeries, tc.batchSize, tc.matchers), func(t *testing.T) { 52 _, ing := newDefaultTestStore(t) 53 userIDs, _ := pushTestSamples(t, ing, tc.numSeries, 100, 0) 54 55 for _, userID := range userIDs { 56 ctx := user.InjectOrgID(context.Background(), userID) 57 instance, ok, err := ing.userStates.getViaContext(ctx) 58 require.NoError(t, err) 59 require.True(t, ok) 60 61 total, batch, batches := 0, 0, 0 62 err = instance.forSeriesMatching(ctx, tc.matchers, 63 func(_ context.Context, _ model.Fingerprint, s *memorySeries) error { 64 batch++ 65 return nil 66 }, 67 func(context.Context) error { 68 require.True(t, batch <= tc.batchSize) 69 total += batch 70 batch = 0 71 batches++ 72 return nil 73 }, 74 tc.batchSize) 75 require.NoError(t, err) 76 require.Equal(t, tc.expected, total) 77 require.Equal(t, int(math.Ceil(float64(tc.expected)/float64(tc.batchSize))), batches) 78 } 79 }) 80 } 81 } 82 83 // TestTeardown ensures metrics are updated correctly if the userState is discarded 84 func TestTeardown(t *testing.T) { 85 reg := prometheus.NewPedanticRegistry() 86 _, ing := newTestStore(t, 87 defaultIngesterTestConfig(t), 88 defaultClientTestConfig(), 89 defaultLimitsTestConfig(), 90 reg) 91 92 pushTestSamples(t, ing, 100, 100, 0) 93 94 // Assert exported metrics (3 blocks, 5 files per block, 2 files WAL). 95 metricNames := []string{ 96 "cortex_ingester_memory_series_created_total", 97 "cortex_ingester_memory_series_removed_total", 98 "cortex_ingester_memory_series", 99 "cortex_ingester_memory_users", 100 "cortex_ingester_active_series", 101 } 102 103 ing.userStatesMtx.Lock() 104 ing.userStates.purgeAndUpdateActiveSeries(time.Now().Add(-5 * time.Minute)) 105 ing.userStatesMtx.Unlock() 106 107 assert.NoError(t, testutil.GatherAndCompare(reg, strings.NewReader(` 108 # HELP cortex_ingester_memory_series_removed_total The total number of series that were removed per user. 109 # TYPE cortex_ingester_memory_series_removed_total counter 110 cortex_ingester_memory_series_removed_total{user="1"} 0 111 cortex_ingester_memory_series_removed_total{user="2"} 0 112 cortex_ingester_memory_series_removed_total{user="3"} 0 113 # HELP cortex_ingester_memory_series_created_total The total number of series that were created per user. 114 # TYPE cortex_ingester_memory_series_created_total counter 115 cortex_ingester_memory_series_created_total{user="1"} 100 116 cortex_ingester_memory_series_created_total{user="2"} 100 117 cortex_ingester_memory_series_created_total{user="3"} 100 118 # HELP cortex_ingester_memory_series The current number of series in memory. 119 # TYPE cortex_ingester_memory_series gauge 120 cortex_ingester_memory_series 300 121 # HELP cortex_ingester_memory_users The current number of users in memory. 122 # TYPE cortex_ingester_memory_users gauge 123 cortex_ingester_memory_users 3 124 # HELP cortex_ingester_active_series Number of currently active series per user. 125 # TYPE cortex_ingester_active_series gauge 126 cortex_ingester_active_series{user="1"} 100 127 cortex_ingester_active_series{user="2"} 100 128 cortex_ingester_active_series{user="3"} 100 129 `), metricNames...)) 130 131 ing.userStatesMtx.Lock() 132 defer ing.userStatesMtx.Unlock() 133 ing.userStates.teardown() 134 135 assert.NoError(t, testutil.GatherAndCompare(reg, strings.NewReader(` 136 # HELP cortex_ingester_memory_series_removed_total The total number of series that were removed per user. 137 # TYPE cortex_ingester_memory_series_removed_total counter 138 cortex_ingester_memory_series_removed_total{user="1"} 100 139 cortex_ingester_memory_series_removed_total{user="2"} 100 140 cortex_ingester_memory_series_removed_total{user="3"} 100 141 # HELP cortex_ingester_memory_series_created_total The total number of series that were created per user. 142 # TYPE cortex_ingester_memory_series_created_total counter 143 cortex_ingester_memory_series_created_total{user="1"} 100 144 cortex_ingester_memory_series_created_total{user="2"} 100 145 cortex_ingester_memory_series_created_total{user="3"} 100 146 # HELP cortex_ingester_memory_series The current number of series in memory. 147 # TYPE cortex_ingester_memory_series gauge 148 cortex_ingester_memory_series 0 149 # HELP cortex_ingester_memory_users The current number of users in memory. 150 # TYPE cortex_ingester_memory_users gauge 151 cortex_ingester_memory_users 0 152 # HELP cortex_ingester_active_series Number of currently active series per user. 153 # TYPE cortex_ingester_active_series gauge 154 cortex_ingester_active_series{user="1"} 0 155 cortex_ingester_active_series{user="2"} 0 156 cortex_ingester_active_series{user="3"} 0 157 `), metricNames...)) 158 } 159 160 func TestMetricCounter(t *testing.T) { 161 const metric = "metric" 162 163 for name, tc := range map[string]struct { 164 ignored []string 165 localLimit int 166 series int 167 expectedErrorOnLastSeries error 168 }{ 169 "no ignored metrics, limit not reached": { 170 ignored: nil, 171 localLimit: 5, 172 series: 5, 173 expectedErrorOnLastSeries: nil, 174 }, 175 176 "no ignored metrics, limit reached": { 177 ignored: nil, 178 localLimit: 5, 179 series: 6, 180 expectedErrorOnLastSeries: errMaxSeriesPerMetricLimitExceeded, 181 }, 182 183 "ignored metric, limit not reached": { 184 ignored: []string{metric}, 185 localLimit: 5, 186 series: 5, 187 expectedErrorOnLastSeries: nil, 188 }, 189 190 "ignored metric, over limit": { 191 ignored: []string{metric}, 192 localLimit: 5, 193 series: 10, 194 expectedErrorOnLastSeries: nil, // this metric is not checked for series-count. 195 }, 196 197 "ignored different metric, limit not reached": { 198 ignored: []string{"another_metric1", "another_metric2"}, 199 localLimit: 5, 200 series: 5, 201 expectedErrorOnLastSeries: nil, 202 }, 203 204 "ignored different metric, over limit": { 205 ignored: []string{"another_metric1", "another_metric2"}, 206 localLimit: 5, 207 series: 6, 208 expectedErrorOnLastSeries: errMaxSeriesPerMetricLimitExceeded, 209 }, 210 } { 211 t.Run(name, func(t *testing.T) { 212 var ignored map[string]struct{} 213 if tc.ignored != nil { 214 ignored = make(map[string]struct{}) 215 for _, v := range tc.ignored { 216 ignored[v] = struct{}{} 217 } 218 } 219 220 limits := validation.Limits{MaxLocalSeriesPerMetric: tc.localLimit} 221 222 overrides, err := validation.NewOverrides(limits, nil) 223 require.NoError(t, err) 224 225 // We're testing code that's not dependant on sharding strategy, replication factor, etc. To simplify the test, 226 // we use local limit only. 227 limiter := NewLimiter(overrides, nil, util.ShardingStrategyDefault, true, 3, false) 228 mc := newMetricCounter(limiter, ignored) 229 230 for i := 0; i < tc.series; i++ { 231 err := mc.canAddSeriesFor("user", metric) 232 if i < tc.series-1 { 233 assert.NoError(t, err) 234 mc.increaseSeriesForMetric(metric) 235 } else { 236 assert.Equal(t, tc.expectedErrorOnLastSeries, err) 237 } 238 } 239 }) 240 } 241 }