github.com/muhammadn/cortex@v1.9.1-0.20220510110439-46bb7000d03d/pkg/compactor/compactor_test.go (about) 1 package compactor 2 3 import ( 4 "bytes" 5 "context" 6 "crypto/rand" 7 "encoding/json" 8 "flag" 9 "fmt" 10 "io/ioutil" 11 "os" 12 "path" 13 "path/filepath" 14 "regexp" 15 "strconv" 16 "strings" 17 "testing" 18 "time" 19 20 "github.com/go-kit/log" 21 "github.com/grafana/dskit/concurrency" 22 "github.com/grafana/dskit/flagext" 23 "github.com/grafana/dskit/kv/consul" 24 "github.com/grafana/dskit/services" 25 "github.com/oklog/ulid" 26 "github.com/pkg/errors" 27 "github.com/prometheus/client_golang/prometheus" 28 prom_testutil "github.com/prometheus/client_golang/prometheus/testutil" 29 "github.com/prometheus/prometheus/pkg/labels" 30 "github.com/prometheus/prometheus/tsdb" 31 "github.com/stretchr/testify/assert" 32 "github.com/stretchr/testify/mock" 33 "github.com/stretchr/testify/require" 34 "github.com/thanos-io/thanos/pkg/block/metadata" 35 "github.com/thanos-io/thanos/pkg/compact" 36 "github.com/thanos-io/thanos/pkg/objstore" 37 "gopkg.in/yaml.v2" 38 39 "github.com/grafana/dskit/ring" 40 41 "github.com/cortexproject/cortex/pkg/storage/bucket" 42 cortex_tsdb "github.com/cortexproject/cortex/pkg/storage/tsdb" 43 cortex_testutil "github.com/cortexproject/cortex/pkg/util/test" 44 "github.com/cortexproject/cortex/pkg/util/validation" 45 ) 46 47 func TestConfig_ShouldSupportYamlConfig(t *testing.T) { 48 yamlCfg := ` 49 block_ranges: [2h, 48h] 50 consistency_delay: 1h 51 block_sync_concurrency: 123 52 data_dir: /tmp 53 compaction_interval: 15m 54 compaction_retries: 123 55 ` 56 57 cfg := Config{} 58 flagext.DefaultValues(&cfg) 59 assert.NoError(t, yaml.Unmarshal([]byte(yamlCfg), &cfg)) 60 assert.Equal(t, cortex_tsdb.DurationList{2 * time.Hour, 48 * time.Hour}, cfg.BlockRanges) 61 assert.Equal(t, time.Hour, cfg.ConsistencyDelay) 62 assert.Equal(t, 123, cfg.BlockSyncConcurrency) 63 assert.Equal(t, "/tmp", cfg.DataDir) 64 assert.Equal(t, 15*time.Minute, cfg.CompactionInterval) 65 assert.Equal(t, 123, cfg.CompactionRetries) 66 } 67 68 func TestConfig_ShouldSupportCliFlags(t *testing.T) { 69 fs := flag.NewFlagSet("", flag.PanicOnError) 70 cfg := Config{} 71 cfg.RegisterFlags(fs) 72 require.NoError(t, fs.Parse([]string{ 73 "-compactor.block-ranges=2h,48h", 74 "-compactor.consistency-delay=1h", 75 "-compactor.block-sync-concurrency=123", 76 "-compactor.data-dir=/tmp", 77 "-compactor.compaction-interval=15m", 78 "-compactor.compaction-retries=123", 79 })) 80 81 assert.Equal(t, cortex_tsdb.DurationList{2 * time.Hour, 48 * time.Hour}, cfg.BlockRanges) 82 assert.Equal(t, time.Hour, cfg.ConsistencyDelay) 83 assert.Equal(t, 123, cfg.BlockSyncConcurrency) 84 assert.Equal(t, "/tmp", cfg.DataDir) 85 assert.Equal(t, 15*time.Minute, cfg.CompactionInterval) 86 assert.Equal(t, 123, cfg.CompactionRetries) 87 } 88 89 func TestConfig_Validate(t *testing.T) { 90 tests := map[string]struct { 91 setup func(cfg *Config) 92 expected string 93 }{ 94 "should pass with the default config": { 95 setup: func(cfg *Config) {}, 96 expected: "", 97 }, 98 "should pass with only 1 block range period": { 99 setup: func(cfg *Config) { 100 cfg.BlockRanges = cortex_tsdb.DurationList{time.Hour} 101 }, 102 expected: "", 103 }, 104 "should fail with non divisible block range periods": { 105 setup: func(cfg *Config) { 106 cfg.BlockRanges = cortex_tsdb.DurationList{2 * time.Hour, 12 * time.Hour, 24 * time.Hour, 30 * time.Hour} 107 }, 108 expected: errors.Errorf(errInvalidBlockRanges, 30*time.Hour, 24*time.Hour).Error(), 109 }, 110 } 111 112 for testName, testData := range tests { 113 t.Run(testName, func(t *testing.T) { 114 cfg := &Config{} 115 flagext.DefaultValues(cfg) 116 testData.setup(cfg) 117 118 if actualErr := cfg.Validate(); testData.expected != "" { 119 assert.EqualError(t, actualErr, testData.expected) 120 } else { 121 assert.NoError(t, actualErr) 122 } 123 }) 124 } 125 } 126 127 func TestCompactor_ShouldDoNothingOnNoUserBlocks(t *testing.T) { 128 t.Parallel() 129 130 // No user blocks stored in the bucket. 131 bucketClient := &bucket.ClientMock{} 132 bucketClient.MockIter("", []string{}, nil) 133 cfg := prepareConfig() 134 c, _, _, logs, registry := prepare(t, cfg, bucketClient) 135 require.NoError(t, services.StartAndAwaitRunning(context.Background(), c)) 136 137 // Wait until a run has completed. 138 cortex_testutil.Poll(t, time.Second, 1.0, func() interface{} { 139 return prom_testutil.ToFloat64(c.compactionRunsCompleted) 140 }) 141 142 require.NoError(t, services.StopAndAwaitTerminated(context.Background(), c)) 143 144 assert.Equal(t, prom_testutil.ToFloat64(c.compactionRunInterval), cfg.CompactionInterval.Seconds()) 145 146 assert.Equal(t, []string{ 147 `level=info component=cleaner msg="started blocks cleanup and maintenance"`, 148 `level=info component=cleaner msg="successfully completed blocks cleanup and maintenance"`, 149 `level=info component=compactor msg="discovering users from bucket"`, 150 `level=info component=compactor msg="discovered users from bucket" users=0`, 151 }, strings.Split(strings.TrimSpace(logs.String()), "\n")) 152 153 assert.NoError(t, prom_testutil.GatherAndCompare(registry, strings.NewReader(` 154 # TYPE cortex_compactor_runs_started_total counter 155 # HELP cortex_compactor_runs_started_total Total number of compaction runs started. 156 cortex_compactor_runs_started_total 1 157 158 # TYPE cortex_compactor_runs_completed_total counter 159 # HELP cortex_compactor_runs_completed_total Total number of compaction runs successfully completed. 160 cortex_compactor_runs_completed_total 1 161 162 # TYPE cortex_compactor_runs_failed_total counter 163 # HELP cortex_compactor_runs_failed_total Total number of compaction runs failed. 164 cortex_compactor_runs_failed_total 0 165 166 # HELP cortex_compactor_garbage_collected_blocks_total Total number of blocks marked for deletion by compactor. 167 # TYPE cortex_compactor_garbage_collected_blocks_total counter 168 cortex_compactor_garbage_collected_blocks_total 0 169 170 # HELP cortex_compactor_garbage_collection_duration_seconds Time it took to perform garbage collection iteration. 171 # TYPE cortex_compactor_garbage_collection_duration_seconds histogram 172 cortex_compactor_garbage_collection_duration_seconds_bucket{le="+Inf"} 0 173 cortex_compactor_garbage_collection_duration_seconds_sum 0 174 cortex_compactor_garbage_collection_duration_seconds_count 0 175 176 # HELP cortex_compactor_garbage_collection_failures_total Total number of failed garbage collection operations. 177 # TYPE cortex_compactor_garbage_collection_failures_total counter 178 cortex_compactor_garbage_collection_failures_total 0 179 180 # HELP cortex_compactor_garbage_collection_total Total number of garbage collection operations. 181 # TYPE cortex_compactor_garbage_collection_total counter 182 cortex_compactor_garbage_collection_total 0 183 184 # HELP cortex_compactor_meta_sync_consistency_delay_seconds Configured consistency delay in seconds. 185 # TYPE cortex_compactor_meta_sync_consistency_delay_seconds gauge 186 cortex_compactor_meta_sync_consistency_delay_seconds 0 187 188 # HELP cortex_compactor_meta_sync_duration_seconds Duration of the blocks metadata synchronization in seconds. 189 # TYPE cortex_compactor_meta_sync_duration_seconds histogram 190 cortex_compactor_meta_sync_duration_seconds_bucket{le="+Inf"} 0 191 cortex_compactor_meta_sync_duration_seconds_sum 0 192 cortex_compactor_meta_sync_duration_seconds_count 0 193 194 # HELP cortex_compactor_meta_sync_failures_total Total blocks metadata synchronization failures. 195 # TYPE cortex_compactor_meta_sync_failures_total counter 196 cortex_compactor_meta_sync_failures_total 0 197 198 # HELP cortex_compactor_meta_syncs_total Total blocks metadata synchronization attempts. 199 # TYPE cortex_compactor_meta_syncs_total counter 200 cortex_compactor_meta_syncs_total 0 201 202 # HELP cortex_compactor_group_compaction_runs_completed_total Total number of group completed compaction runs. This also includes compactor group runs that resulted with no compaction. 203 # TYPE cortex_compactor_group_compaction_runs_completed_total counter 204 cortex_compactor_group_compaction_runs_completed_total 0 205 206 # HELP cortex_compactor_group_compaction_runs_started_total Total number of group compaction attempts. 207 # TYPE cortex_compactor_group_compaction_runs_started_total counter 208 cortex_compactor_group_compaction_runs_started_total 0 209 210 # HELP cortex_compactor_group_compactions_failures_total Total number of failed group compactions. 211 # TYPE cortex_compactor_group_compactions_failures_total counter 212 cortex_compactor_group_compactions_failures_total 0 213 214 # HELP cortex_compactor_group_compactions_total Total number of group compaction attempts that resulted in a new block. 215 # TYPE cortex_compactor_group_compactions_total counter 216 cortex_compactor_group_compactions_total 0 217 218 # HELP cortex_compactor_group_vertical_compactions_total Total number of group compaction attempts that resulted in a new block based on overlapping blocks. 219 # TYPE cortex_compactor_group_vertical_compactions_total counter 220 cortex_compactor_group_vertical_compactions_total 0 221 222 # TYPE cortex_compactor_block_cleanup_failures_total counter 223 # HELP cortex_compactor_block_cleanup_failures_total Total number of blocks failed to be deleted. 224 cortex_compactor_block_cleanup_failures_total 0 225 226 # HELP cortex_compactor_blocks_cleaned_total Total number of blocks deleted. 227 # TYPE cortex_compactor_blocks_cleaned_total counter 228 cortex_compactor_blocks_cleaned_total 0 229 230 # HELP cortex_compactor_blocks_marked_for_deletion_total Total number of blocks marked for deletion in compactor. 231 # TYPE cortex_compactor_blocks_marked_for_deletion_total counter 232 cortex_compactor_blocks_marked_for_deletion_total{reason="compaction"} 0 233 cortex_compactor_blocks_marked_for_deletion_total{reason="retention"} 0 234 235 # TYPE cortex_compactor_block_cleanup_started_total counter 236 # HELP cortex_compactor_block_cleanup_started_total Total number of blocks cleanup runs started. 237 cortex_compactor_block_cleanup_started_total 1 238 239 # TYPE cortex_compactor_block_cleanup_completed_total counter 240 # HELP cortex_compactor_block_cleanup_completed_total Total number of blocks cleanup runs successfully completed. 241 cortex_compactor_block_cleanup_completed_total 1 242 243 # TYPE cortex_compactor_block_cleanup_failed_total counter 244 # HELP cortex_compactor_block_cleanup_failed_total Total number of blocks cleanup runs failed. 245 cortex_compactor_block_cleanup_failed_total 0 246 `), 247 "cortex_compactor_runs_started_total", 248 "cortex_compactor_runs_completed_total", 249 "cortex_compactor_runs_failed_total", 250 "cortex_compactor_garbage_collected_blocks_total", 251 "cortex_compactor_garbage_collection_duration_seconds", 252 "cortex_compactor_garbage_collection_failures_total", 253 "cortex_compactor_garbage_collection_total", 254 "cortex_compactor_meta_sync_consistency_delay_seconds", 255 "cortex_compactor_meta_sync_duration_seconds", 256 "cortex_compactor_meta_sync_failures_total", 257 "cortex_compactor_meta_syncs_total", 258 "cortex_compactor_group_compaction_runs_completed_total", 259 "cortex_compactor_group_compaction_runs_started_total", 260 "cortex_compactor_group_compactions_failures_total", 261 "cortex_compactor_group_compactions_total", 262 "cortex_compactor_group_vertical_compactions_total", 263 "cortex_compactor_block_cleanup_failures_total", 264 "cortex_compactor_blocks_cleaned_total", 265 "cortex_compactor_blocks_marked_for_deletion_total", 266 "cortex_compactor_block_cleanup_started_total", 267 "cortex_compactor_block_cleanup_completed_total", 268 "cortex_compactor_block_cleanup_failed_total", 269 )) 270 } 271 272 func TestCompactor_ShouldRetryCompactionOnFailureWhileDiscoveringUsersFromBucket(t *testing.T) { 273 t.Parallel() 274 275 // Fail to iterate over the bucket while discovering users. 276 bucketClient := &bucket.ClientMock{} 277 bucketClient.MockIter("", nil, errors.New("failed to iterate the bucket")) 278 279 c, _, _, logs, registry := prepare(t, prepareConfig(), bucketClient) 280 require.NoError(t, services.StartAndAwaitRunning(context.Background(), c)) 281 282 // Wait until all retry attempts have completed. 283 cortex_testutil.Poll(t, time.Second, 1.0, func() interface{} { 284 return prom_testutil.ToFloat64(c.compactionRunsFailed) 285 }) 286 287 require.NoError(t, services.StopAndAwaitTerminated(context.Background(), c)) 288 289 // Ensure the bucket iteration has been retried the configured number of times. 290 bucketClient.AssertNumberOfCalls(t, "Iter", 1+3) 291 292 assert.Equal(t, []string{ 293 `level=info component=cleaner msg="started blocks cleanup and maintenance"`, 294 `level=error component=cleaner msg="failed to run blocks cleanup and maintenance" err="failed to discover users from bucket: failed to iterate the bucket"`, 295 `level=info component=compactor msg="discovering users from bucket"`, 296 `level=error component=compactor msg="failed to discover users from bucket" err="failed to iterate the bucket"`, 297 }, strings.Split(strings.TrimSpace(logs.String()), "\n")) 298 299 assert.NoError(t, prom_testutil.GatherAndCompare(registry, strings.NewReader(` 300 # TYPE cortex_compactor_runs_started_total counter 301 # HELP cortex_compactor_runs_started_total Total number of compaction runs started. 302 cortex_compactor_runs_started_total 1 303 304 # TYPE cortex_compactor_runs_completed_total counter 305 # HELP cortex_compactor_runs_completed_total Total number of compaction runs successfully completed. 306 cortex_compactor_runs_completed_total 0 307 308 # TYPE cortex_compactor_runs_failed_total counter 309 # HELP cortex_compactor_runs_failed_total Total number of compaction runs failed. 310 cortex_compactor_runs_failed_total 1 311 312 # HELP cortex_compactor_garbage_collected_blocks_total Total number of blocks marked for deletion by compactor. 313 # TYPE cortex_compactor_garbage_collected_blocks_total counter 314 cortex_compactor_garbage_collected_blocks_total 0 315 316 # HELP cortex_compactor_garbage_collection_duration_seconds Time it took to perform garbage collection iteration. 317 # TYPE cortex_compactor_garbage_collection_duration_seconds histogram 318 cortex_compactor_garbage_collection_duration_seconds_bucket{le="+Inf"} 0 319 cortex_compactor_garbage_collection_duration_seconds_sum 0 320 cortex_compactor_garbage_collection_duration_seconds_count 0 321 322 # HELP cortex_compactor_garbage_collection_failures_total Total number of failed garbage collection operations. 323 # TYPE cortex_compactor_garbage_collection_failures_total counter 324 cortex_compactor_garbage_collection_failures_total 0 325 326 # HELP cortex_compactor_garbage_collection_total Total number of garbage collection operations. 327 # TYPE cortex_compactor_garbage_collection_total counter 328 cortex_compactor_garbage_collection_total 0 329 330 # HELP cortex_compactor_meta_sync_consistency_delay_seconds Configured consistency delay in seconds. 331 # TYPE cortex_compactor_meta_sync_consistency_delay_seconds gauge 332 cortex_compactor_meta_sync_consistency_delay_seconds 0 333 334 # HELP cortex_compactor_meta_sync_duration_seconds Duration of the blocks metadata synchronization in seconds. 335 # TYPE cortex_compactor_meta_sync_duration_seconds histogram 336 cortex_compactor_meta_sync_duration_seconds_bucket{le="+Inf"} 0 337 cortex_compactor_meta_sync_duration_seconds_sum 0 338 cortex_compactor_meta_sync_duration_seconds_count 0 339 340 # HELP cortex_compactor_meta_sync_failures_total Total blocks metadata synchronization failures. 341 # TYPE cortex_compactor_meta_sync_failures_total counter 342 cortex_compactor_meta_sync_failures_total 0 343 344 # HELP cortex_compactor_meta_syncs_total Total blocks metadata synchronization attempts. 345 # TYPE cortex_compactor_meta_syncs_total counter 346 cortex_compactor_meta_syncs_total 0 347 348 # HELP cortex_compactor_group_compaction_runs_completed_total Total number of group completed compaction runs. This also includes compactor group runs that resulted with no compaction. 349 # TYPE cortex_compactor_group_compaction_runs_completed_total counter 350 cortex_compactor_group_compaction_runs_completed_total 0 351 352 # HELP cortex_compactor_group_compaction_runs_started_total Total number of group compaction attempts. 353 # TYPE cortex_compactor_group_compaction_runs_started_total counter 354 cortex_compactor_group_compaction_runs_started_total 0 355 356 # HELP cortex_compactor_group_compactions_failures_total Total number of failed group compactions. 357 # TYPE cortex_compactor_group_compactions_failures_total counter 358 cortex_compactor_group_compactions_failures_total 0 359 360 # HELP cortex_compactor_group_compactions_total Total number of group compaction attempts that resulted in a new block. 361 # TYPE cortex_compactor_group_compactions_total counter 362 cortex_compactor_group_compactions_total 0 363 364 # HELP cortex_compactor_group_vertical_compactions_total Total number of group compaction attempts that resulted in a new block based on overlapping blocks. 365 # TYPE cortex_compactor_group_vertical_compactions_total counter 366 cortex_compactor_group_vertical_compactions_total 0 367 368 # TYPE cortex_compactor_block_cleanup_failures_total counter 369 # HELP cortex_compactor_block_cleanup_failures_total Total number of blocks failed to be deleted. 370 cortex_compactor_block_cleanup_failures_total 0 371 372 # HELP cortex_compactor_blocks_cleaned_total Total number of blocks deleted. 373 # TYPE cortex_compactor_blocks_cleaned_total counter 374 cortex_compactor_blocks_cleaned_total 0 375 376 # HELP cortex_compactor_blocks_marked_for_deletion_total Total number of blocks marked for deletion in compactor. 377 # TYPE cortex_compactor_blocks_marked_for_deletion_total counter 378 cortex_compactor_blocks_marked_for_deletion_total{reason="compaction"} 0 379 cortex_compactor_blocks_marked_for_deletion_total{reason="retention"} 0 380 381 # TYPE cortex_compactor_block_cleanup_started_total counter 382 # HELP cortex_compactor_block_cleanup_started_total Total number of blocks cleanup runs started. 383 cortex_compactor_block_cleanup_started_total 1 384 385 # TYPE cortex_compactor_block_cleanup_completed_total counter 386 # HELP cortex_compactor_block_cleanup_completed_total Total number of blocks cleanup runs successfully completed. 387 cortex_compactor_block_cleanup_completed_total 0 388 389 # TYPE cortex_compactor_block_cleanup_failed_total counter 390 # HELP cortex_compactor_block_cleanup_failed_total Total number of blocks cleanup runs failed. 391 cortex_compactor_block_cleanup_failed_total 1 392 `), 393 "cortex_compactor_runs_started_total", 394 "cortex_compactor_runs_completed_total", 395 "cortex_compactor_runs_failed_total", 396 "cortex_compactor_garbage_collected_blocks_total", 397 "cortex_compactor_garbage_collection_duration_seconds", 398 "cortex_compactor_garbage_collection_failures_total", 399 "cortex_compactor_garbage_collection_total", 400 "cortex_compactor_meta_sync_consistency_delay_seconds", 401 "cortex_compactor_meta_sync_duration_seconds", 402 "cortex_compactor_meta_sync_failures_total", 403 "cortex_compactor_meta_syncs_total", 404 "cortex_compactor_group_compaction_runs_completed_total", 405 "cortex_compactor_group_compaction_runs_started_total", 406 "cortex_compactor_group_compactions_failures_total", 407 "cortex_compactor_group_compactions_total", 408 "cortex_compactor_group_vertical_compactions_total", 409 "cortex_compactor_block_cleanup_failures_total", 410 "cortex_compactor_blocks_cleaned_total", 411 "cortex_compactor_blocks_marked_for_deletion_total", 412 "cortex_compactor_block_cleanup_started_total", 413 "cortex_compactor_block_cleanup_completed_total", 414 "cortex_compactor_block_cleanup_failed_total", 415 )) 416 } 417 418 func TestCompactor_ShouldIncrementCompactionErrorIfFailedToCompactASingleTenant(t *testing.T) { 419 t.Parallel() 420 421 userID := "test-user" 422 bucketClient := &bucket.ClientMock{} 423 bucketClient.MockIter("", []string{userID}, nil) 424 bucketClient.MockIter(userID+"/", []string{userID + "/01DTVP434PA9VFXSW2JKB3392D"}, nil) 425 bucketClient.MockIter(userID+"/markers/", nil, nil) 426 bucketClient.MockExists(path.Join(userID, cortex_tsdb.TenantDeletionMarkPath), false, nil) 427 bucketClient.MockGet(userID+"/01DTVP434PA9VFXSW2JKB3392D/meta.json", mockBlockMetaJSON("01DTVP434PA9VFXSW2JKB3392D"), nil) 428 bucketClient.MockGet(userID+"/01DTVP434PA9VFXSW2JKB3392D/deletion-mark.json", "", nil) 429 bucketClient.MockGet(userID+"/bucket-index.json.gz", "", nil) 430 bucketClient.MockUpload(userID+"/bucket-index.json.gz", nil) 431 432 c, _, tsdbPlannerMock, _, registry := prepare(t, prepareConfig(), bucketClient) 433 tsdbPlannerMock.On("Plan", mock.Anything, mock.Anything).Return([]*metadata.Meta{}, errors.New("Failed to plan")) 434 require.NoError(t, services.StartAndAwaitRunning(context.Background(), c)) 435 436 // Wait until all retry attempts have completed. 437 cortex_testutil.Poll(t, time.Second, 1.0, func() interface{} { 438 return prom_testutil.ToFloat64(c.compactionRunsFailed) 439 }) 440 441 require.NoError(t, services.StopAndAwaitTerminated(context.Background(), c)) 442 443 assert.NoError(t, prom_testutil.GatherAndCompare(registry, strings.NewReader(` 444 # TYPE cortex_compactor_runs_started_total counter 445 # HELP cortex_compactor_runs_started_total Total number of compaction runs started. 446 cortex_compactor_runs_started_total 1 447 448 # TYPE cortex_compactor_runs_completed_total counter 449 # HELP cortex_compactor_runs_completed_total Total number of compaction runs successfully completed. 450 cortex_compactor_runs_completed_total 0 451 452 # TYPE cortex_compactor_runs_failed_total counter 453 # HELP cortex_compactor_runs_failed_total Total number of compaction runs failed. 454 cortex_compactor_runs_failed_total 1 455 `), 456 "cortex_compactor_runs_started_total", 457 "cortex_compactor_runs_completed_total", 458 "cortex_compactor_runs_failed_total", 459 )) 460 } 461 462 func TestCompactor_ShouldIterateOverUsersAndRunCompaction(t *testing.T) { 463 t.Parallel() 464 465 // Mock the bucket to contain two users, each one with one block. 466 bucketClient := &bucket.ClientMock{} 467 bucketClient.MockIter("", []string{"user-1", "user-2"}, nil) 468 bucketClient.MockExists(path.Join("user-1", cortex_tsdb.TenantDeletionMarkPath), false, nil) 469 bucketClient.MockExists(path.Join("user-2", cortex_tsdb.TenantDeletionMarkPath), false, nil) 470 bucketClient.MockIter("user-1/", []string{"user-1/01DTVP434PA9VFXSW2JKB3392D"}, nil) 471 bucketClient.MockIter("user-2/", []string{"user-2/01DTW0ZCPDDNV4BV83Q2SV4QAZ"}, nil) 472 bucketClient.MockGet("user-1/01DTVP434PA9VFXSW2JKB3392D/meta.json", mockBlockMetaJSON("01DTVP434PA9VFXSW2JKB3392D"), nil) 473 bucketClient.MockGet("user-1/01DTVP434PA9VFXSW2JKB3392D/deletion-mark.json", "", nil) 474 bucketClient.MockGet("user-2/01DTW0ZCPDDNV4BV83Q2SV4QAZ/meta.json", mockBlockMetaJSON("01DTW0ZCPDDNV4BV83Q2SV4QAZ"), nil) 475 bucketClient.MockGet("user-2/01DTW0ZCPDDNV4BV83Q2SV4QAZ/deletion-mark.json", "", nil) 476 bucketClient.MockGet("user-1/bucket-index.json.gz", "", nil) 477 bucketClient.MockGet("user-2/bucket-index.json.gz", "", nil) 478 bucketClient.MockIter("user-1/markers/", nil, nil) 479 bucketClient.MockIter("user-2/markers/", nil, nil) 480 bucketClient.MockUpload("user-1/bucket-index.json.gz", nil) 481 bucketClient.MockUpload("user-2/bucket-index.json.gz", nil) 482 483 c, _, tsdbPlanner, logs, registry := prepare(t, prepareConfig(), bucketClient) 484 485 // Mock the planner as if there's no compaction to do, 486 // in order to simplify tests (all in all, we just want to 487 // test our logic and not TSDB compactor which we expect to 488 // be already tested). 489 tsdbPlanner.On("Plan", mock.Anything, mock.Anything).Return([]*metadata.Meta{}, nil) 490 491 require.NoError(t, services.StartAndAwaitRunning(context.Background(), c)) 492 493 // Wait until a run has completed. 494 cortex_testutil.Poll(t, time.Second, 1.0, func() interface{} { 495 return prom_testutil.ToFloat64(c.compactionRunsCompleted) 496 }) 497 498 require.NoError(t, services.StopAndAwaitTerminated(context.Background(), c)) 499 500 // Ensure a plan has been executed for the blocks of each user. 501 tsdbPlanner.AssertNumberOfCalls(t, "Plan", 2) 502 503 assert.ElementsMatch(t, []string{ 504 `level=info component=cleaner msg="started blocks cleanup and maintenance"`, 505 `level=info component=cleaner org_id=user-1 msg="started blocks cleanup and maintenance"`, 506 `level=info component=cleaner org_id=user-1 msg="completed blocks cleanup and maintenance"`, 507 `level=info component=cleaner org_id=user-2 msg="started blocks cleanup and maintenance"`, 508 `level=info component=cleaner org_id=user-2 msg="completed blocks cleanup and maintenance"`, 509 `level=info component=cleaner msg="successfully completed blocks cleanup and maintenance"`, 510 `level=info component=compactor msg="discovering users from bucket"`, 511 `level=info component=compactor msg="discovered users from bucket" users=2`, 512 `level=info component=compactor msg="starting compaction of user blocks" user=user-1`, 513 `level=info component=compactor org_id=user-1 msg="start sync of metas"`, 514 `level=info component=compactor org_id=user-1 msg="start of GC"`, 515 `level=info component=compactor org_id=user-1 msg="start of compactions"`, 516 `level=info component=compactor org_id=user-1 msg="compaction iterations done"`, 517 `level=info component=compactor msg="successfully compacted user blocks" user=user-1`, 518 `level=info component=compactor msg="starting compaction of user blocks" user=user-2`, 519 `level=info component=compactor org_id=user-2 msg="start sync of metas"`, 520 `level=info component=compactor org_id=user-2 msg="start of GC"`, 521 `level=info component=compactor org_id=user-2 msg="start of compactions"`, 522 `level=info component=compactor org_id=user-2 msg="compaction iterations done"`, 523 `level=info component=compactor msg="successfully compacted user blocks" user=user-2`, 524 }, removeIgnoredLogs(strings.Split(strings.TrimSpace(logs.String()), "\n"))) 525 526 // Instead of testing for shipper metrics, we only check our metrics here. 527 // Real shipper metrics are too variable to embed into a test. 528 testedMetrics := []string{ 529 "cortex_compactor_runs_started_total", "cortex_compactor_runs_completed_total", "cortex_compactor_runs_failed_total", 530 "cortex_compactor_blocks_cleaned_total", "cortex_compactor_block_cleanup_failures_total", "cortex_compactor_blocks_marked_for_deletion_total", 531 "cortex_compactor_block_cleanup_started_total", "cortex_compactor_block_cleanup_completed_total", "cortex_compactor_block_cleanup_failed_total", 532 } 533 assert.NoError(t, prom_testutil.GatherAndCompare(registry, strings.NewReader(` 534 # TYPE cortex_compactor_runs_started_total counter 535 # HELP cortex_compactor_runs_started_total Total number of compaction runs started. 536 cortex_compactor_runs_started_total 1 537 538 # TYPE cortex_compactor_runs_completed_total counter 539 # HELP cortex_compactor_runs_completed_total Total number of compaction runs successfully completed. 540 cortex_compactor_runs_completed_total 1 541 542 # TYPE cortex_compactor_runs_failed_total counter 543 # HELP cortex_compactor_runs_failed_total Total number of compaction runs failed. 544 cortex_compactor_runs_failed_total 0 545 546 # TYPE cortex_compactor_block_cleanup_failures_total counter 547 # HELP cortex_compactor_block_cleanup_failures_total Total number of blocks failed to be deleted. 548 cortex_compactor_block_cleanup_failures_total 0 549 550 # HELP cortex_compactor_blocks_cleaned_total Total number of blocks deleted. 551 # TYPE cortex_compactor_blocks_cleaned_total counter 552 cortex_compactor_blocks_cleaned_total 0 553 554 # HELP cortex_compactor_blocks_marked_for_deletion_total Total number of blocks marked for deletion in compactor. 555 # TYPE cortex_compactor_blocks_marked_for_deletion_total counter 556 cortex_compactor_blocks_marked_for_deletion_total{reason="compaction"} 0 557 cortex_compactor_blocks_marked_for_deletion_total{reason="retention"} 0 558 559 # TYPE cortex_compactor_block_cleanup_started_total counter 560 # HELP cortex_compactor_block_cleanup_started_total Total number of blocks cleanup runs started. 561 cortex_compactor_block_cleanup_started_total 1 562 563 # TYPE cortex_compactor_block_cleanup_completed_total counter 564 # HELP cortex_compactor_block_cleanup_completed_total Total number of blocks cleanup runs successfully completed. 565 cortex_compactor_block_cleanup_completed_total 1 566 567 # TYPE cortex_compactor_block_cleanup_failed_total counter 568 # HELP cortex_compactor_block_cleanup_failed_total Total number of blocks cleanup runs failed. 569 cortex_compactor_block_cleanup_failed_total 0 570 `), testedMetrics...)) 571 } 572 573 func TestCompactor_ShouldNotCompactBlocksMarkedForDeletion(t *testing.T) { 574 t.Parallel() 575 576 cfg := prepareConfig() 577 cfg.DeletionDelay = 10 * time.Minute // Delete block after 10 minutes 578 579 // Mock the bucket to contain two users, each one with one block. 580 bucketClient := &bucket.ClientMock{} 581 bucketClient.MockIter("", []string{"user-1"}, nil) 582 bucketClient.MockIter("user-1/", []string{"user-1/01DTVP434PA9VFXSW2JKB3392D", "user-1/01DTW0ZCPDDNV4BV83Q2SV4QAZ"}, nil) 583 bucketClient.MockExists(path.Join("user-1", cortex_tsdb.TenantDeletionMarkPath), false, nil) 584 585 // Block that has just been marked for deletion. It will not be deleted just yet, and it also will not be compacted. 586 bucketClient.MockGet("user-1/01DTVP434PA9VFXSW2JKB3392D/meta.json", mockBlockMetaJSON("01DTVP434PA9VFXSW2JKB3392D"), nil) 587 bucketClient.MockGet("user-1/01DTVP434PA9VFXSW2JKB3392D/deletion-mark.json", mockDeletionMarkJSON("01DTVP434PA9VFXSW2JKB3392D", time.Now()), nil) 588 bucketClient.MockGet("user-1/markers/01DTVP434PA9VFXSW2JKB3392D-deletion-mark.json", mockDeletionMarkJSON("01DTVP434PA9VFXSW2JKB3392D", time.Now()), nil) 589 590 // This block will be deleted by cleaner. 591 bucketClient.MockGet("user-1/01DTW0ZCPDDNV4BV83Q2SV4QAZ/meta.json", mockBlockMetaJSON("01DTW0ZCPDDNV4BV83Q2SV4QAZ"), nil) 592 bucketClient.MockGet("user-1/01DTW0ZCPDDNV4BV83Q2SV4QAZ/deletion-mark.json", mockDeletionMarkJSON("01DTW0ZCPDDNV4BV83Q2SV4QAZ", time.Now().Add(-cfg.DeletionDelay)), nil) 593 bucketClient.MockGet("user-1/markers/01DTW0ZCPDDNV4BV83Q2SV4QAZ-deletion-mark.json", mockDeletionMarkJSON("01DTW0ZCPDDNV4BV83Q2SV4QAZ", time.Now().Add(-cfg.DeletionDelay)), nil) 594 595 bucketClient.MockIter("user-1/01DTW0ZCPDDNV4BV83Q2SV4QAZ", []string{ 596 "user-1/01DTW0ZCPDDNV4BV83Q2SV4QAZ/meta.json", 597 "user-1/01DTW0ZCPDDNV4BV83Q2SV4QAZ/deletion-mark.json", 598 }, nil) 599 600 bucketClient.MockIter("user-1/markers/", []string{ 601 "user-1/markers/01DTVP434PA9VFXSW2JKB3392D-deletion-mark.json", 602 "user-1/markers/01DTW0ZCPDDNV4BV83Q2SV4QAZ-deletion-mark.json", 603 }, nil) 604 605 bucketClient.MockDelete("user-1/01DTW0ZCPDDNV4BV83Q2SV4QAZ/meta.json", nil) 606 bucketClient.MockDelete("user-1/01DTW0ZCPDDNV4BV83Q2SV4QAZ/deletion-mark.json", nil) 607 bucketClient.MockDelete("user-1/markers/01DTW0ZCPDDNV4BV83Q2SV4QAZ-deletion-mark.json", nil) 608 bucketClient.MockDelete("user-1/01DTW0ZCPDDNV4BV83Q2SV4QAZ", nil) 609 bucketClient.MockGet("user-1/bucket-index.json.gz", "", nil) 610 bucketClient.MockUpload("user-1/bucket-index.json.gz", nil) 611 612 c, _, tsdbPlanner, logs, registry := prepare(t, cfg, bucketClient) 613 614 require.NoError(t, services.StartAndAwaitRunning(context.Background(), c)) 615 616 // Wait until a run has completed. 617 cortex_testutil.Poll(t, time.Second, 1.0, func() interface{} { 618 return prom_testutil.ToFloat64(c.compactionRunsCompleted) 619 }) 620 621 require.NoError(t, services.StopAndAwaitTerminated(context.Background(), c)) 622 623 // Since both blocks are marked for deletion, none of them are going to be compacted. 624 tsdbPlanner.AssertNumberOfCalls(t, "Plan", 0) 625 626 assert.ElementsMatch(t, []string{ 627 `level=info component=cleaner msg="started blocks cleanup and maintenance"`, 628 `level=info component=cleaner org_id=user-1 msg="started blocks cleanup and maintenance"`, 629 `level=debug component=cleaner org_id=user-1 msg="deleted file" file=01DTW0ZCPDDNV4BV83Q2SV4QAZ/meta.json bucket=mock`, 630 `level=debug component=cleaner org_id=user-1 msg="deleted file" file=01DTW0ZCPDDNV4BV83Q2SV4QAZ/deletion-mark.json bucket=mock`, 631 `level=info component=cleaner org_id=user-1 msg="deleted block marked for deletion" block=01DTW0ZCPDDNV4BV83Q2SV4QAZ`, 632 `level=info component=cleaner org_id=user-1 msg="completed blocks cleanup and maintenance"`, 633 `level=info component=cleaner msg="successfully completed blocks cleanup and maintenance"`, 634 `level=info component=compactor msg="discovering users from bucket"`, 635 `level=info component=compactor msg="discovered users from bucket" users=1`, 636 `level=info component=compactor msg="starting compaction of user blocks" user=user-1`, 637 `level=info component=compactor org_id=user-1 msg="start sync of metas"`, 638 `level=info component=compactor org_id=user-1 msg="start of GC"`, 639 `level=info component=compactor org_id=user-1 msg="start of compactions"`, 640 `level=info component=compactor org_id=user-1 msg="compaction iterations done"`, 641 `level=info component=compactor msg="successfully compacted user blocks" user=user-1`, 642 }, removeIgnoredLogs(strings.Split(strings.TrimSpace(logs.String()), "\n"))) 643 644 // Instead of testing for shipper metrics, we only check our metrics here. 645 // Real shipper metrics are too variable to embed into a test. 646 testedMetrics := []string{ 647 "cortex_compactor_runs_started_total", "cortex_compactor_runs_completed_total", "cortex_compactor_runs_failed_total", 648 "cortex_compactor_blocks_cleaned_total", "cortex_compactor_block_cleanup_failures_total", "cortex_compactor_blocks_marked_for_deletion_total", 649 "cortex_compactor_block_cleanup_started_total", "cortex_compactor_block_cleanup_completed_total", "cortex_compactor_block_cleanup_failed_total", 650 } 651 assert.NoError(t, prom_testutil.GatherAndCompare(registry, strings.NewReader(` 652 # TYPE cortex_compactor_runs_started_total counter 653 # HELP cortex_compactor_runs_started_total Total number of compaction runs started. 654 cortex_compactor_runs_started_total 1 655 656 # TYPE cortex_compactor_runs_completed_total counter 657 # HELP cortex_compactor_runs_completed_total Total number of compaction runs successfully completed. 658 cortex_compactor_runs_completed_total 1 659 660 # TYPE cortex_compactor_runs_failed_total counter 661 # HELP cortex_compactor_runs_failed_total Total number of compaction runs failed. 662 cortex_compactor_runs_failed_total 0 663 664 # TYPE cortex_compactor_block_cleanup_failures_total counter 665 # HELP cortex_compactor_block_cleanup_failures_total Total number of blocks failed to be deleted. 666 cortex_compactor_block_cleanup_failures_total 0 667 668 # HELP cortex_compactor_blocks_cleaned_total Total number of blocks deleted. 669 # TYPE cortex_compactor_blocks_cleaned_total counter 670 cortex_compactor_blocks_cleaned_total 1 671 672 # HELP cortex_compactor_blocks_marked_for_deletion_total Total number of blocks marked for deletion in compactor. 673 # TYPE cortex_compactor_blocks_marked_for_deletion_total counter 674 cortex_compactor_blocks_marked_for_deletion_total{reason="compaction"} 0 675 cortex_compactor_blocks_marked_for_deletion_total{reason="retention"} 0 676 677 # TYPE cortex_compactor_block_cleanup_started_total counter 678 # HELP cortex_compactor_block_cleanup_started_total Total number of blocks cleanup runs started. 679 cortex_compactor_block_cleanup_started_total 1 680 681 # TYPE cortex_compactor_block_cleanup_completed_total counter 682 # HELP cortex_compactor_block_cleanup_completed_total Total number of blocks cleanup runs successfully completed. 683 cortex_compactor_block_cleanup_completed_total 1 684 685 # TYPE cortex_compactor_block_cleanup_failed_total counter 686 # HELP cortex_compactor_block_cleanup_failed_total Total number of blocks cleanup runs failed. 687 cortex_compactor_block_cleanup_failed_total 0 688 `), testedMetrics...)) 689 } 690 691 func TestCompactor_ShouldNotCompactBlocksForUsersMarkedForDeletion(t *testing.T) { 692 t.Parallel() 693 694 cfg := prepareConfig() 695 cfg.DeletionDelay = 10 * time.Minute // Delete block after 10 minutes 696 cfg.TenantCleanupDelay = 10 * time.Minute // To make sure it's not 0. 697 698 // Mock the bucket to contain two users, each one with one block. 699 bucketClient := &bucket.ClientMock{} 700 bucketClient.MockIter("", []string{"user-1"}, nil) 701 bucketClient.MockIter("user-1/", []string{"user-1/01DTVP434PA9VFXSW2JKB3392D"}, nil) 702 bucketClient.MockGet(path.Join("user-1", cortex_tsdb.TenantDeletionMarkPath), `{"deletion_time": 1}`, nil) 703 bucketClient.MockUpload(path.Join("user-1", cortex_tsdb.TenantDeletionMarkPath), nil) 704 705 bucketClient.MockIter("user-1/01DTVP434PA9VFXSW2JKB3392D", []string{"user-1/01DTVP434PA9VFXSW2JKB3392D/meta.json", "user-1/01DTVP434PA9VFXSW2JKB3392D/index"}, nil) 706 bucketClient.MockGet("user-1/01DTVP434PA9VFXSW2JKB3392D/meta.json", mockBlockMetaJSON("01DTVP434PA9VFXSW2JKB3392D"), nil) 707 bucketClient.MockGet("user-1/01DTVP434PA9VFXSW2JKB3392D/index", "some index content", nil) 708 bucketClient.MockExists("user-1/01DTVP434PA9VFXSW2JKB3392D/deletion-mark.json", false, nil) 709 710 bucketClient.MockDelete("user-1/01DTVP434PA9VFXSW2JKB3392D/meta.json", nil) 711 bucketClient.MockDelete("user-1/01DTVP434PA9VFXSW2JKB3392D/index", nil) 712 bucketClient.MockDelete("user-1/bucket-index.json.gz", nil) 713 714 c, _, tsdbPlanner, logs, registry := prepare(t, cfg, bucketClient) 715 716 // Mock the planner as if there's no compaction to do, 717 // in order to simplify tests (all in all, we just want to 718 // test our logic and not TSDB compactor which we expect to 719 // be already tested). 720 tsdbPlanner.On("Plan", mock.Anything, mock.Anything).Return([]*metadata.Meta{}, nil) 721 722 require.NoError(t, services.StartAndAwaitRunning(context.Background(), c)) 723 724 // Wait until a run has completed. 725 cortex_testutil.Poll(t, time.Second, 1.0, func() interface{} { 726 return prom_testutil.ToFloat64(c.compactionRunsCompleted) 727 }) 728 729 require.NoError(t, services.StopAndAwaitTerminated(context.Background(), c)) 730 731 // No user is compacted, single user we have is marked for deletion. 732 tsdbPlanner.AssertNumberOfCalls(t, "Plan", 0) 733 734 assert.ElementsMatch(t, []string{ 735 `level=info component=cleaner msg="started blocks cleanup and maintenance"`, 736 `level=info component=cleaner org_id=user-1 msg="deleting blocks for tenant marked for deletion"`, 737 `level=debug component=cleaner org_id=user-1 msg="deleted file" file=01DTVP434PA9VFXSW2JKB3392D/meta.json bucket=mock`, 738 `level=debug component=cleaner org_id=user-1 msg="deleted file" file=01DTVP434PA9VFXSW2JKB3392D/index bucket=mock`, 739 `level=info component=cleaner org_id=user-1 msg="deleted block" block=01DTVP434PA9VFXSW2JKB3392D`, 740 `level=info component=cleaner org_id=user-1 msg="deleted blocks for tenant marked for deletion" deletedBlocks=1`, 741 `level=info component=cleaner org_id=user-1 msg="updating finished time in tenant deletion mark"`, 742 `level=info component=cleaner msg="successfully completed blocks cleanup and maintenance"`, 743 `level=info component=compactor msg="discovering users from bucket"`, 744 `level=info component=compactor msg="discovered users from bucket" users=1`, 745 `level=debug component=compactor msg="skipping user because it is marked for deletion" user=user-1`, 746 }, removeIgnoredLogs(strings.Split(strings.TrimSpace(logs.String()), "\n"))) 747 748 // Instead of testing for shipper metrics, we only check our metrics here. 749 // Real shipper metrics are too variable to embed into a test. 750 testedMetrics := []string{ 751 "cortex_compactor_runs_started_total", "cortex_compactor_runs_completed_total", "cortex_compactor_runs_failed_total", 752 "cortex_compactor_blocks_cleaned_total", "cortex_compactor_block_cleanup_failures_total", "cortex_compactor_blocks_marked_for_deletion_total", 753 "cortex_compactor_block_cleanup_started_total", "cortex_compactor_block_cleanup_completed_total", "cortex_compactor_block_cleanup_failed_total", 754 "cortex_bucket_blocks_count", "cortex_bucket_blocks_marked_for_deletion_count", "cortex_bucket_index_last_successful_update_timestamp_seconds", 755 } 756 assert.NoError(t, prom_testutil.GatherAndCompare(registry, strings.NewReader(` 757 # TYPE cortex_compactor_runs_started_total counter 758 # HELP cortex_compactor_runs_started_total Total number of compaction runs started. 759 cortex_compactor_runs_started_total 1 760 761 # TYPE cortex_compactor_runs_completed_total counter 762 # HELP cortex_compactor_runs_completed_total Total number of compaction runs successfully completed. 763 cortex_compactor_runs_completed_total 1 764 765 # TYPE cortex_compactor_runs_failed_total counter 766 # HELP cortex_compactor_runs_failed_total Total number of compaction runs failed. 767 cortex_compactor_runs_failed_total 0 768 769 # TYPE cortex_compactor_block_cleanup_failures_total counter 770 # HELP cortex_compactor_block_cleanup_failures_total Total number of blocks failed to be deleted. 771 cortex_compactor_block_cleanup_failures_total 0 772 773 # HELP cortex_compactor_blocks_cleaned_total Total number of blocks deleted. 774 # TYPE cortex_compactor_blocks_cleaned_total counter 775 cortex_compactor_blocks_cleaned_total 1 776 777 # HELP cortex_compactor_blocks_marked_for_deletion_total Total number of blocks marked for deletion in compactor. 778 # TYPE cortex_compactor_blocks_marked_for_deletion_total counter 779 cortex_compactor_blocks_marked_for_deletion_total{reason="compaction"} 0 780 cortex_compactor_blocks_marked_for_deletion_total{reason="retention"} 0 781 782 # TYPE cortex_compactor_block_cleanup_started_total counter 783 # HELP cortex_compactor_block_cleanup_started_total Total number of blocks cleanup runs started. 784 cortex_compactor_block_cleanup_started_total 1 785 786 # TYPE cortex_compactor_block_cleanup_completed_total counter 787 # HELP cortex_compactor_block_cleanup_completed_total Total number of blocks cleanup runs successfully completed. 788 cortex_compactor_block_cleanup_completed_total 1 789 790 # TYPE cortex_compactor_block_cleanup_failed_total counter 791 # HELP cortex_compactor_block_cleanup_failed_total Total number of blocks cleanup runs failed. 792 cortex_compactor_block_cleanup_failed_total 0 793 `), testedMetrics...)) 794 } 795 796 func TestCompactor_ShouldCompactAllUsersOnShardingEnabledButOnlyOneInstanceRunning(t *testing.T) { 797 t.Parallel() 798 799 // Mock the bucket to contain two users, each one with one block. 800 bucketClient := &bucket.ClientMock{} 801 bucketClient.MockIter("", []string{"user-1", "user-2"}, nil) 802 bucketClient.MockExists(path.Join("user-1", cortex_tsdb.TenantDeletionMarkPath), false, nil) 803 bucketClient.MockExists(path.Join("user-2", cortex_tsdb.TenantDeletionMarkPath), false, nil) 804 bucketClient.MockIter("user-1/", []string{"user-1/01DTVP434PA9VFXSW2JKB3392D"}, nil) 805 bucketClient.MockIter("user-2/", []string{"user-2/01DTW0ZCPDDNV4BV83Q2SV4QAZ"}, nil) 806 bucketClient.MockIter("user-1/markers/", nil, nil) 807 bucketClient.MockIter("user-2/markers/", nil, nil) 808 bucketClient.MockGet("user-1/01DTVP434PA9VFXSW2JKB3392D/meta.json", mockBlockMetaJSON("01DTVP434PA9VFXSW2JKB3392D"), nil) 809 bucketClient.MockGet("user-1/01DTVP434PA9VFXSW2JKB3392D/deletion-mark.json", "", nil) 810 bucketClient.MockGet("user-2/01DTW0ZCPDDNV4BV83Q2SV4QAZ/meta.json", mockBlockMetaJSON("01DTW0ZCPDDNV4BV83Q2SV4QAZ"), nil) 811 bucketClient.MockGet("user-2/01DTW0ZCPDDNV4BV83Q2SV4QAZ/deletion-mark.json", "", nil) 812 bucketClient.MockGet("user-1/bucket-index.json.gz", "", nil) 813 bucketClient.MockGet("user-2/bucket-index.json.gz", "", nil) 814 bucketClient.MockUpload("user-1/bucket-index.json.gz", nil) 815 bucketClient.MockUpload("user-2/bucket-index.json.gz", nil) 816 817 ringStore, closer := consul.NewInMemoryClient(ring.GetCodec(), log.NewNopLogger(), nil) 818 t.Cleanup(func() { assert.NoError(t, closer.Close()) }) 819 820 cfg := prepareConfig() 821 cfg.ShardingEnabled = true 822 cfg.ShardingRing.InstanceID = "compactor-1" 823 cfg.ShardingRing.InstanceAddr = "1.2.3.4" 824 cfg.ShardingRing.KVStore.Mock = ringStore 825 826 c, _, tsdbPlanner, logs, _ := prepare(t, cfg, bucketClient) 827 828 // Mock the planner as if there's no compaction to do, 829 // in order to simplify tests (all in all, we just want to 830 // test our logic and not TSDB compactor which we expect to 831 // be already tested). 832 tsdbPlanner.On("Plan", mock.Anything, mock.Anything).Return([]*metadata.Meta{}, nil) 833 834 require.NoError(t, services.StartAndAwaitRunning(context.Background(), c)) 835 836 // Wait until a run has completed. 837 cortex_testutil.Poll(t, 5*time.Second, 1.0, func() interface{} { 838 return prom_testutil.ToFloat64(c.compactionRunsCompleted) 839 }) 840 841 require.NoError(t, services.StopAndAwaitTerminated(context.Background(), c)) 842 843 // Ensure a plan has been executed for the blocks of each user. 844 tsdbPlanner.AssertNumberOfCalls(t, "Plan", 2) 845 846 assert.ElementsMatch(t, []string{ 847 `level=info component=compactor msg="waiting until compactor is ACTIVE in the ring"`, 848 `level=info component=compactor msg="compactor is ACTIVE in the ring"`, 849 `level=info component=cleaner msg="started blocks cleanup and maintenance"`, 850 `level=info component=cleaner org_id=user-1 msg="started blocks cleanup and maintenance"`, 851 `level=info component=cleaner org_id=user-1 msg="completed blocks cleanup and maintenance"`, 852 `level=info component=cleaner org_id=user-2 msg="started blocks cleanup and maintenance"`, 853 `level=info component=cleaner org_id=user-2 msg="completed blocks cleanup and maintenance"`, 854 `level=info component=cleaner msg="successfully completed blocks cleanup and maintenance"`, 855 `level=info component=compactor msg="discovering users from bucket"`, 856 `level=info component=compactor msg="discovered users from bucket" users=2`, 857 `level=info component=compactor msg="starting compaction of user blocks" user=user-1`, 858 `level=info component=compactor org_id=user-1 msg="start sync of metas"`, 859 `level=info component=compactor org_id=user-1 msg="start of GC"`, 860 `level=info component=compactor org_id=user-1 msg="start of compactions"`, 861 `level=info component=compactor org_id=user-1 msg="compaction iterations done"`, 862 `level=info component=compactor msg="successfully compacted user blocks" user=user-1`, 863 `level=info component=compactor msg="starting compaction of user blocks" user=user-2`, 864 `level=info component=compactor org_id=user-2 msg="start sync of metas"`, 865 `level=info component=compactor org_id=user-2 msg="start of GC"`, 866 `level=info component=compactor org_id=user-2 msg="start of compactions"`, 867 `level=info component=compactor org_id=user-2 msg="compaction iterations done"`, 868 `level=info component=compactor msg="successfully compacted user blocks" user=user-2`, 869 }, removeIgnoredLogs(strings.Split(strings.TrimSpace(logs.String()), "\n"))) 870 } 871 872 func TestCompactor_ShouldCompactOnlyUsersOwnedByTheInstanceOnShardingEnabledAndMultipleInstancesRunning(t *testing.T) { 873 t.Parallel() 874 875 numUsers := 100 876 877 // Setup user IDs 878 userIDs := make([]string, 0, numUsers) 879 for i := 1; i <= numUsers; i++ { 880 userIDs = append(userIDs, fmt.Sprintf("user-%d", i)) 881 } 882 883 // Mock the bucket to contain all users, each one with one block. 884 bucketClient := &bucket.ClientMock{} 885 bucketClient.MockIter("", userIDs, nil) 886 for _, userID := range userIDs { 887 bucketClient.MockIter(userID+"/", []string{userID + "/01DTVP434PA9VFXSW2JKB3392D"}, nil) 888 bucketClient.MockIter(userID+"/markers/", nil, nil) 889 bucketClient.MockExists(path.Join(userID, cortex_tsdb.TenantDeletionMarkPath), false, nil) 890 bucketClient.MockGet(userID+"/01DTVP434PA9VFXSW2JKB3392D/meta.json", mockBlockMetaJSON("01DTVP434PA9VFXSW2JKB3392D"), nil) 891 bucketClient.MockGet(userID+"/01DTVP434PA9VFXSW2JKB3392D/deletion-mark.json", "", nil) 892 bucketClient.MockGet(userID+"/bucket-index.json.gz", "", nil) 893 bucketClient.MockUpload(userID+"/bucket-index.json.gz", nil) 894 } 895 896 // Create a shared KV Store 897 kvstore, closer := consul.NewInMemoryClient(ring.GetCodec(), log.NewNopLogger(), nil) 898 t.Cleanup(func() { assert.NoError(t, closer.Close()) }) 899 900 // Create two compactors 901 var compactors []*Compactor 902 var logs []*concurrency.SyncBuffer 903 904 for i := 1; i <= 2; i++ { 905 cfg := prepareConfig() 906 cfg.ShardingEnabled = true 907 cfg.ShardingRing.InstanceID = fmt.Sprintf("compactor-%d", i) 908 cfg.ShardingRing.InstanceAddr = fmt.Sprintf("127.0.0.%d", i) 909 cfg.ShardingRing.WaitStabilityMinDuration = 3 * time.Second 910 cfg.ShardingRing.WaitStabilityMaxDuration = 10 * time.Second 911 cfg.ShardingRing.KVStore.Mock = kvstore 912 913 c, _, tsdbPlanner, l, _ := prepare(t, cfg, bucketClient) 914 defer services.StopAndAwaitTerminated(context.Background(), c) //nolint:errcheck 915 916 compactors = append(compactors, c) 917 logs = append(logs, l) 918 919 // Mock the planner as if there's no compaction to do, 920 // in order to simplify tests (all in all, we just want to 921 // test our logic and not TSDB compactor which we expect to 922 // be already tested). 923 tsdbPlanner.On("Plan", mock.Anything, mock.Anything).Return([]*metadata.Meta{}, nil) 924 } 925 926 // Start all compactors 927 for _, c := range compactors { 928 require.NoError(t, services.StartAndAwaitRunning(context.Background(), c)) 929 } 930 931 // Wait until a run has been completed on each compactor 932 for _, c := range compactors { 933 cortex_testutil.Poll(t, 10*time.Second, 1.0, func() interface{} { 934 return prom_testutil.ToFloat64(c.compactionRunsCompleted) 935 }) 936 } 937 938 // Ensure that each user has been compacted by the correct instance 939 for _, userID := range userIDs { 940 _, l, err := findCompactorByUserID(compactors, logs, userID) 941 require.NoError(t, err) 942 assert.Contains(t, l.String(), fmt.Sprintf(`level=info component=compactor msg="successfully compacted user blocks" user=%s`, userID)) 943 } 944 } 945 946 func createTSDBBlock(t *testing.T, bkt objstore.Bucket, userID string, minT, maxT int64, externalLabels map[string]string) ulid.ULID { 947 // Create a temporary dir for TSDB. 948 tempDir, err := ioutil.TempDir(os.TempDir(), "tsdb") 949 require.NoError(t, err) 950 defer os.RemoveAll(tempDir) //nolint:errcheck 951 952 // Create a temporary dir for the snapshot. 953 snapshotDir, err := ioutil.TempDir(os.TempDir(), "snapshot") 954 require.NoError(t, err) 955 defer os.RemoveAll(snapshotDir) //nolint:errcheck 956 957 // Create a new TSDB. 958 db, err := tsdb.Open(tempDir, nil, nil, &tsdb.Options{ 959 MinBlockDuration: int64(2 * 60 * 60 * 1000), // 2h period 960 MaxBlockDuration: int64(2 * 60 * 60 * 1000), // 2h period 961 RetentionDuration: int64(15 * 86400 * 1000), // 15 days 962 }, nil) 963 require.NoError(t, err) 964 965 db.DisableCompactions() 966 967 // Append a sample at the beginning and one at the end of the time range. 968 for i, ts := range []int64{minT, maxT - 1} { 969 lbls := labels.Labels{labels.Label{Name: "series_id", Value: strconv.Itoa(i)}} 970 971 app := db.Appender(context.Background()) 972 _, err := app.Append(0, lbls, ts, float64(i)) 973 require.NoError(t, err) 974 975 err = app.Commit() 976 require.NoError(t, err) 977 } 978 979 require.NoError(t, db.Compact()) 980 require.NoError(t, db.Snapshot(snapshotDir, true)) 981 982 // Look for the created block (we expect one). 983 entries, err := ioutil.ReadDir(snapshotDir) 984 require.NoError(t, err) 985 require.Len(t, entries, 1) 986 require.True(t, entries[0].IsDir()) 987 988 blockID, err := ulid.Parse(entries[0].Name()) 989 require.NoError(t, err) 990 991 // Inject Thanos external labels to the block. 992 meta := metadata.Thanos{ 993 Labels: externalLabels, 994 Source: "test", 995 } 996 _, err = metadata.InjectThanos(log.NewNopLogger(), filepath.Join(snapshotDir, blockID.String()), meta, nil) 997 require.NoError(t, err) 998 999 // Copy the block files to the bucket. 1000 srcRoot := filepath.Join(snapshotDir, blockID.String()) 1001 require.NoError(t, filepath.Walk(srcRoot, func(file string, info os.FileInfo, err error) error { 1002 if err != nil { 1003 return err 1004 } 1005 if info.IsDir() { 1006 return nil 1007 } 1008 1009 // Read the file content in memory. 1010 content, err := ioutil.ReadFile(file) 1011 if err != nil { 1012 return err 1013 } 1014 1015 // Upload it to the bucket. 1016 relPath, err := filepath.Rel(srcRoot, file) 1017 if err != nil { 1018 return err 1019 } 1020 1021 return bkt.Upload(context.Background(), path.Join(userID, blockID.String(), relPath), bytes.NewReader(content)) 1022 })) 1023 1024 return blockID 1025 } 1026 1027 func createDeletionMark(t *testing.T, bkt objstore.Bucket, userID string, blockID ulid.ULID, deletionTime time.Time) { 1028 content := mockDeletionMarkJSON(blockID.String(), deletionTime) 1029 blockPath := path.Join(userID, blockID.String()) 1030 markPath := path.Join(blockPath, metadata.DeletionMarkFilename) 1031 1032 require.NoError(t, bkt.Upload(context.Background(), markPath, strings.NewReader(content))) 1033 } 1034 1035 func findCompactorByUserID(compactors []*Compactor, logs []*concurrency.SyncBuffer, userID string) (*Compactor, *concurrency.SyncBuffer, error) { 1036 var compactor *Compactor 1037 var log *concurrency.SyncBuffer 1038 1039 for i, c := range compactors { 1040 owned, err := c.ownUser(userID) 1041 if err != nil { 1042 return nil, nil, err 1043 } 1044 1045 // Ensure the user is not owned by multiple compactors 1046 if owned && compactor != nil { 1047 return nil, nil, fmt.Errorf("user %s owned by multiple compactors", userID) 1048 } 1049 if owned { 1050 compactor = c 1051 log = logs[i] 1052 } 1053 } 1054 1055 // Return an error if we've not been able to find it 1056 if compactor == nil { 1057 return nil, nil, fmt.Errorf("user %s not owned by any compactor", userID) 1058 } 1059 1060 return compactor, log, nil 1061 } 1062 1063 func removeIgnoredLogs(input []string) []string { 1064 ignoredLogStringsMap := map[string]struct{}{ 1065 // Since we moved to the component logger from the global logger for the ring in dskit these lines are now expected but are just ring setup information. 1066 `level=info component=compactor msg="ring doesn't exist in KV store yet"`: {}, 1067 `level=info component=compactor msg="not loading tokens from file, tokens file path is empty"`: {}, 1068 `level=info component=compactor msg="instance not found in ring, adding with no tokens" ring=compactor`: {}, 1069 `level=debug component=compactor msg="JoinAfter expired" ring=compactor`: {}, 1070 `level=info component=compactor msg="auto-joining cluster after timeout" ring=compactor`: {}, 1071 `level=info component=compactor msg="lifecycler loop() exited gracefully" ring=compactor`: {}, 1072 `level=info component=compactor msg="changing instance state from" old_state=ACTIVE new_state=LEAVING ring=compactor`: {}, 1073 `level=error component=compactor msg="failed to set state to LEAVING" ring=compactor err="Changing instance state from LEAVING -> LEAVING is disallowed"`: {}, 1074 `level=error component=compactor msg="failed to set state to LEAVING" ring=compactor err="Changing instance state from JOINING -> LEAVING is disallowed"`: {}, 1075 `level=debug component=compactor msg="unregistering instance from ring" ring=compactor`: {}, 1076 `level=info component=compactor msg="instance removed from the KV store" ring=compactor`: {}, 1077 `level=info component=compactor msg="observing tokens before going ACTIVE" ring=compactor`: {}, 1078 } 1079 1080 out := make([]string, 0, len(input)) 1081 durationRe := regexp.MustCompile(`\s?duration=\S+`) 1082 1083 for i := 0; i < len(input); i++ { 1084 log := input[i] 1085 if strings.Contains(log, "block.MetaFetcher") || strings.Contains(log, "block.BaseFetcher") { 1086 continue 1087 } 1088 1089 if _, exists := ignoredLogStringsMap[log]; exists { 1090 continue 1091 } 1092 1093 // Remove any duration from logs. 1094 log = durationRe.ReplaceAllString(log, "") 1095 1096 out = append(out, log) 1097 } 1098 1099 return out 1100 } 1101 1102 func prepareConfig() Config { 1103 compactorCfg := Config{} 1104 flagext.DefaultValues(&compactorCfg) 1105 1106 compactorCfg.retryMinBackoff = 0 1107 compactorCfg.retryMaxBackoff = 0 1108 1109 // The migration is tested in a dedicated test. 1110 compactorCfg.BlockDeletionMarksMigrationEnabled = false 1111 1112 // Do not wait for ring stability by default, in order to speed up tests. 1113 compactorCfg.ShardingRing.WaitStabilityMinDuration = 0 1114 compactorCfg.ShardingRing.WaitStabilityMaxDuration = 0 1115 1116 // Set lower timeout for waiting on compactor to become ACTIVE in the ring for unit tests 1117 compactorCfg.ShardingRing.WaitActiveInstanceTimeout = 5 * time.Second 1118 1119 return compactorCfg 1120 } 1121 1122 func prepare(t *testing.T, compactorCfg Config, bucketClient objstore.Bucket) (*Compactor, *tsdbCompactorMock, *tsdbPlannerMock, *concurrency.SyncBuffer, prometheus.Gatherer) { 1123 storageCfg := cortex_tsdb.BlocksStorageConfig{} 1124 flagext.DefaultValues(&storageCfg) 1125 1126 // Create a temporary directory for compactor data. 1127 dataDir, err := ioutil.TempDir(os.TempDir(), "compactor-test") 1128 require.NoError(t, err) 1129 1130 compactorCfg.DataDir = dataDir 1131 t.Cleanup(func() { 1132 require.NoError(t, os.RemoveAll(dataDir)) 1133 }) 1134 1135 tsdbCompactor := &tsdbCompactorMock{} 1136 tsdbPlanner := &tsdbPlannerMock{} 1137 logs := &concurrency.SyncBuffer{} 1138 logger := log.NewLogfmtLogger(logs) 1139 registry := prometheus.NewRegistry() 1140 1141 var limits validation.Limits 1142 flagext.DefaultValues(&limits) 1143 overrides, err := validation.NewOverrides(limits, nil) 1144 require.NoError(t, err) 1145 1146 bucketClientFactory := func(ctx context.Context) (objstore.Bucket, error) { 1147 return bucketClient, nil 1148 } 1149 1150 blocksCompactorFactory := func(ctx context.Context, cfg Config, logger log.Logger, reg prometheus.Registerer) (compact.Compactor, compact.Planner, error) { 1151 return tsdbCompactor, tsdbPlanner, nil 1152 } 1153 1154 c, err := newCompactor(compactorCfg, storageCfg, overrides, logger, registry, bucketClientFactory, DefaultBlocksGrouperFactory, blocksCompactorFactory) 1155 require.NoError(t, err) 1156 1157 return c, tsdbCompactor, tsdbPlanner, logs, registry 1158 } 1159 1160 type tsdbCompactorMock struct { 1161 mock.Mock 1162 } 1163 1164 func (m *tsdbCompactorMock) Plan(dir string) ([]string, error) { 1165 args := m.Called(dir) 1166 return args.Get(0).([]string), args.Error(1) 1167 } 1168 1169 func (m *tsdbCompactorMock) Write(dest string, b tsdb.BlockReader, mint, maxt int64, parent *tsdb.BlockMeta) (ulid.ULID, error) { 1170 args := m.Called(dest, b, mint, maxt, parent) 1171 return args.Get(0).(ulid.ULID), args.Error(1) 1172 } 1173 1174 func (m *tsdbCompactorMock) Compact(dest string, dirs []string, open []*tsdb.Block) (ulid.ULID, error) { 1175 args := m.Called(dest, dirs, open) 1176 return args.Get(0).(ulid.ULID), args.Error(1) 1177 } 1178 1179 type tsdbPlannerMock struct { 1180 mock.Mock 1181 } 1182 1183 func (m *tsdbPlannerMock) Plan(ctx context.Context, metasByMinTime []*metadata.Meta) ([]*metadata.Meta, error) { 1184 args := m.Called(ctx, metasByMinTime) 1185 return args.Get(0).([]*metadata.Meta), args.Error(1) 1186 } 1187 1188 func mockBlockMetaJSON(id string) string { 1189 meta := tsdb.BlockMeta{ 1190 Version: 1, 1191 ULID: ulid.MustParse(id), 1192 MinTime: 1574776800000, 1193 MaxTime: 1574784000000, 1194 Compaction: tsdb.BlockMetaCompaction{ 1195 Level: 1, 1196 Sources: []ulid.ULID{ulid.MustParse(id)}, 1197 }, 1198 } 1199 1200 content, err := json.Marshal(meta) 1201 if err != nil { 1202 panic("failed to marshal mocked block meta") 1203 } 1204 1205 return string(content) 1206 } 1207 1208 func mockDeletionMarkJSON(id string, deletionTime time.Time) string { 1209 meta := metadata.DeletionMark{ 1210 Version: metadata.DeletionMarkVersion1, 1211 ID: ulid.MustParse(id), 1212 DeletionTime: deletionTime.Unix(), 1213 } 1214 1215 content, err := json.Marshal(meta) 1216 if err != nil { 1217 panic("failed to marshal mocked block meta") 1218 } 1219 1220 return string(content) 1221 } 1222 1223 func TestCompactor_DeleteLocalSyncFiles(t *testing.T) { 1224 numUsers := 10 1225 1226 // Setup user IDs 1227 userIDs := make([]string, 0, numUsers) 1228 for i := 1; i <= numUsers; i++ { 1229 userIDs = append(userIDs, fmt.Sprintf("user-%d", i)) 1230 } 1231 1232 inmem := objstore.NewInMemBucket() 1233 for _, userID := range userIDs { 1234 id, err := ulid.New(ulid.Now(), rand.Reader) 1235 require.NoError(t, err) 1236 require.NoError(t, inmem.Upload(context.Background(), userID+"/"+id.String()+"/meta.json", strings.NewReader(mockBlockMetaJSON(id.String())))) 1237 } 1238 1239 // Create a shared KV Store 1240 kvstore, closer := consul.NewInMemoryClient(ring.GetCodec(), log.NewNopLogger(), nil) 1241 t.Cleanup(func() { assert.NoError(t, closer.Close()) }) 1242 1243 // Create two compactors 1244 var compactors []*Compactor 1245 1246 for i := 1; i <= 2; i++ { 1247 cfg := prepareConfig() 1248 cfg.CompactionInterval = 10 * time.Minute // We will only call compaction manually. 1249 1250 cfg.ShardingEnabled = true 1251 cfg.ShardingRing.InstanceID = fmt.Sprintf("compactor-%d", i) 1252 cfg.ShardingRing.InstanceAddr = fmt.Sprintf("127.0.0.%d", i) 1253 cfg.ShardingRing.WaitStabilityMinDuration = 3 * time.Second 1254 cfg.ShardingRing.WaitStabilityMaxDuration = 10 * time.Second 1255 cfg.ShardingRing.KVStore.Mock = kvstore 1256 1257 // Each compactor will get its own temp dir for storing local files. 1258 c, _, tsdbPlanner, _, _ := prepare(t, cfg, inmem) 1259 t.Cleanup(func() { 1260 require.NoError(t, services.StopAndAwaitTerminated(context.Background(), c)) 1261 }) 1262 1263 compactors = append(compactors, c) 1264 1265 // Mock the planner as if there's no compaction to do, 1266 // in order to simplify tests (all in all, we just want to 1267 // test our logic and not TSDB compactor which we expect to 1268 // be already tested). 1269 tsdbPlanner.On("Plan", mock.Anything, mock.Anything).Return([]*metadata.Meta{}, nil) 1270 } 1271 1272 require.Equal(t, 2, len(compactors)) 1273 c1 := compactors[0] 1274 c2 := compactors[1] 1275 1276 // Start first compactor 1277 require.NoError(t, services.StartAndAwaitRunning(context.Background(), c1)) 1278 1279 // Wait until a run has been completed on first compactor. This happens as soon as compactor starts. 1280 cortex_testutil.Poll(t, 10*time.Second, 1.0, func() interface{} { 1281 return prom_testutil.ToFloat64(c1.compactionRunsCompleted) 1282 }) 1283 1284 require.NoError(t, os.Mkdir(c1.metaSyncDirForUser("new-user"), 0600)) 1285 1286 // Verify that first compactor has synced all the users, plus there is one extra we have just created. 1287 require.Equal(t, numUsers+1, len(c1.listTenantsWithMetaSyncDirectories())) 1288 1289 // Now start second compactor, and wait until it runs compaction. 1290 require.NoError(t, services.StartAndAwaitRunning(context.Background(), c2)) 1291 cortex_testutil.Poll(t, 10*time.Second, 1.0, func() interface{} { 1292 return prom_testutil.ToFloat64(c2.compactionRunsCompleted) 1293 }) 1294 1295 // Let's check how many users second compactor has. 1296 c2Users := len(c2.listTenantsWithMetaSyncDirectories()) 1297 require.NotZero(t, c2Users) 1298 1299 // Force new compaction cycle on first compactor. It will run the cleanup of un-owned users at the end of compaction cycle. 1300 c1.compactUsers(context.Background()) 1301 c1Users := len(c1.listTenantsWithMetaSyncDirectories()) 1302 1303 // Now compactor 1 should have cleaned old sync files. 1304 require.NotEqual(t, numUsers, c1Users) 1305 require.Equal(t, numUsers, c1Users+c2Users) 1306 } 1307 1308 func TestCompactor_ShouldFailCompactionOnTimeout(t *testing.T) { 1309 t.Parallel() 1310 1311 // Mock the bucket 1312 bucketClient := &bucket.ClientMock{} 1313 bucketClient.MockIter("", []string{}, nil) 1314 1315 ringStore, closer := consul.NewInMemoryClient(ring.GetCodec(), log.NewNopLogger(), nil) 1316 t.Cleanup(func() { assert.NoError(t, closer.Close()) }) 1317 1318 cfg := prepareConfig() 1319 cfg.ShardingEnabled = true 1320 cfg.ShardingRing.InstanceID = "compactor-1" 1321 cfg.ShardingRing.InstanceAddr = "1.2.3.4" 1322 cfg.ShardingRing.KVStore.Mock = ringStore 1323 1324 // Set ObservePeriod to longer than the timeout period to mock a timeout while waiting on ring to become ACTIVE 1325 cfg.ShardingRing.ObservePeriod = time.Second * 10 1326 1327 c, _, _, logs, _ := prepare(t, cfg, bucketClient) 1328 1329 // Try to start the compactor with a bad consul kv-store. The 1330 err := services.StartAndAwaitRunning(context.Background(), c) 1331 1332 // Assert that the compactor timed out 1333 assert.Equal(t, context.DeadlineExceeded, err) 1334 1335 assert.ElementsMatch(t, []string{ 1336 `level=info component=compactor msg="waiting until compactor is ACTIVE in the ring"`, 1337 `level=error component=compactor msg="compactor failed to become ACTIVE in the ring" err="context deadline exceeded"`, 1338 }, removeIgnoredLogs(strings.Split(strings.TrimSpace(logs.String()), "\n"))) 1339 }