github.com/thanos-io/thanos@v0.32.5/pkg/compact/planner_test.go (about) 1 // Copyright (c) The Thanos Authors. 2 // Licensed under the Apache License 2.0. 3 4 package compact 5 6 import ( 7 "bytes" 8 "context" 9 "os" 10 "path/filepath" 11 "sort" 12 "testing" 13 14 "github.com/go-kit/log" 15 "github.com/oklog/ulid" 16 "github.com/pkg/errors" 17 "github.com/prometheus/client_golang/prometheus" 18 "github.com/prometheus/client_golang/prometheus/promauto" 19 promtest "github.com/prometheus/client_golang/prometheus/testutil" 20 "github.com/prometheus/prometheus/tsdb" 21 "github.com/thanos-io/objstore" 22 23 "github.com/efficientgo/core/testutil" 24 "github.com/thanos-io/thanos/pkg/block" 25 "github.com/thanos-io/thanos/pkg/block/metadata" 26 ) 27 28 type tsdbPlannerAdapter struct { 29 dir string 30 comp tsdb.Compactor 31 } 32 33 func (p *tsdbPlannerAdapter) Plan(_ context.Context, metasByMinTime []*metadata.Meta, errChan chan error, _ any) ([]*metadata.Meta, error) { 34 // TSDB planning works based on the meta.json files in the given dir. Mock it up. 35 for _, meta := range metasByMinTime { 36 bdir := filepath.Join(p.dir, meta.ULID.String()) 37 if err := os.MkdirAll(bdir, 0777); err != nil { 38 return nil, errors.Wrap(err, "create planning block dir") 39 } 40 if err := meta.WriteToDir(log.NewNopLogger(), bdir); err != nil { 41 return nil, errors.Wrap(err, "write planning meta file") 42 } 43 } 44 plan, err := p.comp.Plan(p.dir) 45 if err != nil { 46 return nil, err 47 } 48 49 var res []*metadata.Meta 50 for _, pdir := range plan { 51 meta, err := metadata.ReadFromDir(pdir) 52 if err != nil { 53 return nil, errors.Wrapf(err, "read meta from %s", pdir) 54 } 55 res = append(res, meta) 56 } 57 return res, nil 58 } 59 60 // Adapted from https://github.com/prometheus/prometheus/blob/6c56a1faaaad07317ff585bda75b99bdba0517ad/tsdb/compact_test.go#L167 61 func TestPlanners_Plan_Compatibility(t *testing.T) { 62 ranges := []int64{ 63 20, 64 60, 65 180, 66 540, 67 1620, 68 } 69 70 // This mimics our default ExponentialBlockRanges with min block size equals to 20. 71 tsdbComp, err := tsdb.NewLeveledCompactor(context.Background(), nil, nil, ranges, nil, nil) 72 testutil.Ok(t, err) 73 tsdbPlanner := &tsdbPlannerAdapter{comp: tsdbComp} 74 tsdbBasedPlanner := NewTSDBBasedPlanner(log.NewNopLogger(), ranges) 75 76 for _, c := range []struct { 77 name string 78 metas []*metadata.Meta 79 expected []*metadata.Meta 80 }{ 81 { 82 name: "Outside range", 83 metas: []*metadata.Meta{ 84 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 20}}, 85 }, 86 }, 87 { 88 name: "We should wait for four blocks of size 20 to appear before compacting.", 89 metas: []*metadata.Meta{ 90 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 20}}, 91 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 92 }, 93 }, 94 { 95 name: `We should wait for a next block of size 20 to appear before compacting 96 the existing ones. We have three, but we ignore the fresh one from WAl`, 97 metas: []*metadata.Meta{ 98 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 20}}, 99 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 100 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(3, nil), MinTime: 40, MaxTime: 60}}, 101 }, 102 }, 103 { 104 name: "Block to fill the entire parent range appeared – should be compacted", 105 metas: []*metadata.Meta{ 106 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 20}}, 107 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 108 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(3, nil), MinTime: 40, MaxTime: 60}}, 109 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(4, nil), MinTime: 60, MaxTime: 80}}, 110 }, 111 expected: []*metadata.Meta{ 112 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 20}}, 113 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 114 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(3, nil), MinTime: 40, MaxTime: 60}}, 115 }, 116 }, 117 { 118 name: "There are blocks to fill the entire 2nd parent range.", 119 metas: []*metadata.Meta{ 120 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(6, nil), MinTime: 0, MaxTime: 60}}, 121 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(7, nil), MinTime: 60, MaxTime: 120}}, 122 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(8, nil), MinTime: 120, MaxTime: 180}}, 123 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(9, nil), MinTime: 180, MaxTime: 200}}, 124 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(10, nil), MinTime: 200, MaxTime: 220}}, 125 }, 126 expected: []*metadata.Meta{ 127 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(6, nil), MinTime: 0, MaxTime: 60}}, 128 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(7, nil), MinTime: 60, MaxTime: 120}}, 129 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(8, nil), MinTime: 120, MaxTime: 180}}, 130 }, 131 }, 132 { 133 name: `Block for the next parent range appeared with gap with size 20. Nothing will happen in the first one 134 anymore but we ignore fresh one still, so no compaction`, 135 metas: []*metadata.Meta{ 136 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 20}}, 137 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 138 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(4, nil), MinTime: 60, MaxTime: 80}}, 139 }, 140 }, 141 { 142 name: `Block for the next parent range appeared, and we have a gap with size 20 between second and third block. 143 We will not get this missed gap anymore and we should compact just these two.`, 144 metas: []*metadata.Meta{ 145 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 20}}, 146 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 147 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(4, nil), MinTime: 60, MaxTime: 80}}, 148 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(5, nil), MinTime: 80, MaxTime: 100}}, 149 }, 150 expected: []*metadata.Meta{ 151 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 20}}, 152 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 153 }, 154 }, 155 { 156 name: "We have 20, 20, 20, 60, 60 range blocks. '5' is marked as fresh one", 157 metas: []*metadata.Meta{ 158 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 20}}, 159 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 160 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(3, nil), MinTime: 40, MaxTime: 60}}, 161 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(4, nil), MinTime: 60, MaxTime: 120}}, 162 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(5, nil), MinTime: 120, MaxTime: 180}}, 163 }, 164 expected: []*metadata.Meta{ 165 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 20}}, 166 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 167 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(3, nil), MinTime: 40, MaxTime: 60}}, 168 }, 169 }, 170 { 171 name: "There are blocks to fill the entire 2nd parent range, but there is a gap", 172 metas: []*metadata.Meta{ 173 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(6, nil), MinTime: 0, MaxTime: 60}}, 174 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(8, nil), MinTime: 120, MaxTime: 180}}, 175 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(9, nil), MinTime: 180, MaxTime: 200}}, 176 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(10, nil), MinTime: 200, MaxTime: 220}}, 177 }, 178 expected: []*metadata.Meta{ 179 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(6, nil), MinTime: 0, MaxTime: 60}}, 180 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(8, nil), MinTime: 120, MaxTime: 180}}, 181 }, 182 }, 183 { 184 name: "We have 20, 60, 20, 60, 240 range blocks. We can compact 20 + 60 + 60", 185 metas: []*metadata.Meta{ 186 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 187 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(4, nil), MinTime: 60, MaxTime: 120}}, 188 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(5, nil), MinTime: 960, MaxTime: 980}}, // Fresh one. 189 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(6, nil), MinTime: 120, MaxTime: 180}}, 190 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(7, nil), MinTime: 720, MaxTime: 960}}, 191 }, 192 expected: []*metadata.Meta{ 193 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 194 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(4, nil), MinTime: 60, MaxTime: 120}}, 195 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(6, nil), MinTime: 120, MaxTime: 180}}, 196 }, 197 }, 198 { 199 name: "Do not select large blocks that have many tombstones when there is no fresh block", 200 metas: []*metadata.Meta{ 201 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 540, Stats: tsdb.BlockStats{ 202 NumSeries: 10, 203 NumTombstones: 3, 204 }}}, 205 }, 206 }, 207 { 208 name: "Select large blocks that have many tombstones when fresh appears", 209 metas: []*metadata.Meta{ 210 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 540, Stats: tsdb.BlockStats{ 211 NumSeries: 10, 212 NumTombstones: 3, 213 }}}, 214 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 540, MaxTime: 560}}, 215 }, 216 expected: []*metadata.Meta{{BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 540, Stats: tsdb.BlockStats{ 217 NumSeries: 10, 218 NumTombstones: 3, 219 }}}}, 220 }, 221 { 222 name: "For small blocks, do not compact tombstones, even when fresh appears.", 223 metas: []*metadata.Meta{ 224 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 60, Stats: tsdb.BlockStats{ 225 NumSeries: 10, 226 NumTombstones: 3, 227 }}}, 228 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 60, MaxTime: 80}}, 229 }, 230 }, 231 { 232 name: `Regression test: we were stuck in a compact loop where we always recompacted 233 the same block when tombstones and series counts were zero`, 234 metas: []*metadata.Meta{ 235 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 540, Stats: tsdb.BlockStats{ 236 NumSeries: 0, 237 NumTombstones: 0, 238 }}}, 239 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 540, MaxTime: 560}}, 240 }, 241 }, 242 { 243 name: `Regression test: we were wrongly assuming that new block is fresh from WAL when its ULID is newest. 244 We need to actually look on max time instead. 245 246 With previous, wrong approach "8" block was ignored, so we were wrongly compacting 5 and 7 and introducing 247 block overlaps`, 248 metas: []*metadata.Meta{ 249 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(5, nil), MinTime: 0, MaxTime: 360}}, 250 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(6, nil), MinTime: 540, MaxTime: 560}}, // Fresh one. 251 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(7, nil), MinTime: 360, MaxTime: 420}}, 252 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(8, nil), MinTime: 420, MaxTime: 540}}, 253 }, 254 expected: []*metadata.Meta{ 255 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(7, nil), MinTime: 360, MaxTime: 420}}, 256 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(8, nil), MinTime: 420, MaxTime: 540}}, 257 }, 258 }, 259 // |--------------| 260 // |----------------| 261 // |--------------| 262 { 263 name: "Overlapping blocks 1", 264 metas: []*metadata.Meta{ 265 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 20}}, 266 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 19, MaxTime: 40}}, 267 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(3, nil), MinTime: 40, MaxTime: 60}}, 268 }, 269 expected: []*metadata.Meta{ 270 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 20}}, 271 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 19, MaxTime: 40}}, 272 }, 273 }, 274 // |--------------| 275 // |--------------| 276 // |--------------| 277 { 278 name: "Overlapping blocks 2", 279 metas: []*metadata.Meta{ 280 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 20}}, 281 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 282 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(3, nil), MinTime: 30, MaxTime: 50}}, 283 }, 284 expected: []*metadata.Meta{ 285 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 286 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(3, nil), MinTime: 30, MaxTime: 50}}, 287 }, 288 }, 289 // |--------------| 290 // |---------------------| 291 // |--------------| 292 { 293 name: "Overlapping blocks 3", 294 metas: []*metadata.Meta{ 295 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 20}}, 296 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 10, MaxTime: 40}}, 297 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(3, nil), MinTime: 30, MaxTime: 50}}, 298 }, 299 expected: []*metadata.Meta{ 300 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 20}}, 301 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 10, MaxTime: 40}}, 302 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(3, nil), MinTime: 30, MaxTime: 50}}, 303 }, 304 }, 305 // |--------------| 306 // |--------------------------------| 307 // |--------------| 308 // |--------------| 309 { 310 name: "Overlapping blocks 4", 311 metas: []*metadata.Meta{ 312 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(5, nil), MinTime: 0, MaxTime: 360}}, 313 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(6, nil), MinTime: 340, MaxTime: 560}}, 314 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(7, nil), MinTime: 360, MaxTime: 420}}, 315 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(8, nil), MinTime: 420, MaxTime: 540}}, 316 }, 317 expected: []*metadata.Meta{ 318 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(5, nil), MinTime: 0, MaxTime: 360}}, 319 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(6, nil), MinTime: 340, MaxTime: 560}}, 320 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(7, nil), MinTime: 360, MaxTime: 420}}, 321 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(8, nil), MinTime: 420, MaxTime: 540}}, 322 }, 323 }, 324 // |--------------| 325 // |--------------| 326 // |--------------| 327 // |--------------| 328 { 329 name: "Overlapping blocks 5", 330 metas: []*metadata.Meta{ 331 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 10}}, 332 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 9, MaxTime: 20}}, 333 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(3, nil), MinTime: 30, MaxTime: 40}}, 334 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(4, nil), MinTime: 39, MaxTime: 50}}, 335 }, 336 expected: []*metadata.Meta{ 337 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 10}}, 338 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 9, MaxTime: 20}}, 339 }, 340 }, 341 } { 342 t.Run(c.name, func(t *testing.T) { 343 for _, e := range c.expected { 344 // Add here to avoid boilerplate. 345 e.Thanos.Labels = make(map[string]string) 346 } 347 for _, e := range c.metas { 348 // Add here to avoid boilerplate. 349 e.Thanos.Labels = make(map[string]string) 350 } 351 352 // For compatibility. 353 t.Run("tsdbPlannerAdapter", func(t *testing.T) { 354 dir, err := os.MkdirTemp("", "test-compact") 355 testutil.Ok(t, err) 356 defer func() { testutil.Ok(t, os.RemoveAll(dir)) }() 357 358 metasByMinTime := make([]*metadata.Meta, len(c.metas)) 359 for i := range metasByMinTime { 360 metasByMinTime[i] = c.metas[i] 361 } 362 sort.Slice(metasByMinTime, func(i, j int) bool { 363 return metasByMinTime[i].MinTime < metasByMinTime[j].MinTime 364 }) 365 366 tsdbPlanner.dir = dir 367 plan, err := tsdbPlanner.Plan(context.Background(), metasByMinTime, nil, nil) 368 testutil.Ok(t, err) 369 testutil.Equals(t, c.expected, plan) 370 }) 371 t.Run("tsdbBasedPlanner", func(t *testing.T) { 372 metasByMinTime := make([]*metadata.Meta, len(c.metas)) 373 for i := range metasByMinTime { 374 metasByMinTime[i] = c.metas[i] 375 } 376 sort.Slice(metasByMinTime, func(i, j int) bool { 377 return metasByMinTime[i].MinTime < metasByMinTime[j].MinTime 378 }) 379 380 plan, err := tsdbBasedPlanner.Plan(context.Background(), metasByMinTime, nil, nil) 381 testutil.Ok(t, err) 382 testutil.Equals(t, c.expected, plan) 383 }) 384 }) 385 } 386 } 387 388 // Adapted form: https://github.com/prometheus/prometheus/blob/6c56a1faaaad07317ff585bda75b99bdba0517ad/tsdb/compact_test.go#L377 389 func TestRangeWithFailedCompactionWontGetSelected(t *testing.T) { 390 ranges := []int64{ 391 20, 392 60, 393 180, 394 540, 395 1620, 396 } 397 398 // This mimics our default ExponentialBlockRanges with min block size equals to 20. 399 tsdbComp, err := tsdb.NewLeveledCompactor(context.Background(), nil, nil, ranges, nil, nil) 400 testutil.Ok(t, err) 401 tsdbPlanner := &tsdbPlannerAdapter{comp: tsdbComp} 402 tsdbBasedPlanner := NewTSDBBasedPlanner(log.NewNopLogger(), ranges) 403 404 for _, c := range []struct { 405 metas []*metadata.Meta 406 }{ 407 { 408 metas: []*metadata.Meta{ 409 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 20}}, 410 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 411 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(3, nil), MinTime: 40, MaxTime: 60}}, 412 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(4, nil), MinTime: 60, MaxTime: 80}}, 413 }, 414 }, 415 { 416 metas: []*metadata.Meta{ 417 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 20}}, 418 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 419 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(4, nil), MinTime: 60, MaxTime: 80}}, 420 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(5, nil), MinTime: 80, MaxTime: 100}}, 421 }, 422 }, 423 { 424 metas: []*metadata.Meta{ 425 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 20}}, 426 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 427 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(3, nil), MinTime: 40, MaxTime: 60}}, 428 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(4, nil), MinTime: 60, MaxTime: 120}}, 429 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(5, nil), MinTime: 120, MaxTime: 180}}, 430 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(6, nil), MinTime: 180, MaxTime: 200}}, 431 }, 432 }, 433 } { 434 t.Run("", func(t *testing.T) { 435 c.metas[1].Compaction.Failed = true 436 // For compatibility. 437 t.Run("tsdbPlannerAdapter", func(t *testing.T) { 438 dir, err := os.MkdirTemp("", "test-compact") 439 testutil.Ok(t, err) 440 defer func() { testutil.Ok(t, os.RemoveAll(dir)) }() 441 442 tsdbPlanner.dir = dir 443 plan, err := tsdbPlanner.Plan(context.Background(), c.metas, nil, nil) 444 testutil.Ok(t, err) 445 testutil.Equals(t, []*metadata.Meta(nil), plan) 446 }) 447 t.Run("tsdbBasedPlanner", func(t *testing.T) { 448 plan, err := tsdbBasedPlanner.Plan(context.Background(), c.metas, nil, nil) 449 testutil.Ok(t, err) 450 testutil.Equals(t, []*metadata.Meta(nil), plan) 451 }) 452 }) 453 } 454 } 455 456 func TestTSDBBasedPlanner_PlanWithNoCompactMarks(t *testing.T) { 457 ranges := []int64{ 458 20, 459 60, 460 180, 461 540, 462 1620, 463 } 464 465 g := &GatherNoCompactionMarkFilter{} 466 tsdbBasedPlanner := NewPlanner(log.NewNopLogger(), ranges, g) 467 468 for _, c := range []struct { 469 name string 470 metas []*metadata.Meta 471 noCompactMarks map[ulid.ULID]*metadata.NoCompactMark 472 473 expected []*metadata.Meta 474 }{ 475 { 476 name: "Outside range and excluded", 477 metas: []*metadata.Meta{ 478 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 20}}, 479 }, 480 noCompactMarks: map[ulid.ULID]*metadata.NoCompactMark{ 481 ulid.MustNew(1, nil): {}, 482 }, 483 }, 484 { 485 name: "Blocks to fill the entire parent, but with first one excluded.", 486 metas: []*metadata.Meta{ 487 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 20}}, 488 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 489 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(3, nil), MinTime: 40, MaxTime: 60}}, 490 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(4, nil), MinTime: 60, MaxTime: 80}}, 491 }, 492 noCompactMarks: map[ulid.ULID]*metadata.NoCompactMark{ 493 ulid.MustNew(1, nil): {}, 494 }, 495 expected: []*metadata.Meta{ 496 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 497 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(3, nil), MinTime: 40, MaxTime: 60}}, 498 }, 499 }, 500 { 501 name: "Blocks to fill the entire parent, but with second one excluded.", 502 metas: []*metadata.Meta{ 503 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 20}}, 504 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 505 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(3, nil), MinTime: 40, MaxTime: 60}}, 506 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(4, nil), MinTime: 60, MaxTime: 80}}, 507 }, 508 noCompactMarks: map[ulid.ULID]*metadata.NoCompactMark{ 509 ulid.MustNew(2, nil): {}, 510 }, 511 }, 512 { 513 name: "Blocks to fill the entire parent, but with last one excluded.", 514 metas: []*metadata.Meta{ 515 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 20}}, 516 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 517 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(3, nil), MinTime: 40, MaxTime: 60}}, 518 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(4, nil), MinTime: 60, MaxTime: 80}}, 519 }, 520 noCompactMarks: map[ulid.ULID]*metadata.NoCompactMark{ 521 ulid.MustNew(4, nil): {}, 522 }, 523 expected: []*metadata.Meta{ 524 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 20}}, 525 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 526 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(3, nil), MinTime: 40, MaxTime: 60}}, 527 }, 528 }, 529 { 530 name: "Blocks to fill the entire parent, but with last one fist excluded.", 531 metas: []*metadata.Meta{ 532 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 20}}, 533 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 534 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(3, nil), MinTime: 40, MaxTime: 60}}, 535 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(4, nil), MinTime: 60, MaxTime: 80}}, 536 }, 537 noCompactMarks: map[ulid.ULID]*metadata.NoCompactMark{ 538 ulid.MustNew(1, nil): {}, 539 ulid.MustNew(4, nil): {}, 540 }, 541 expected: []*metadata.Meta{ 542 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 543 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(3, nil), MinTime: 40, MaxTime: 60}}, 544 }, 545 }, 546 { 547 name: "Blocks to fill the entire parent, but with all of them excluded.", 548 metas: []*metadata.Meta{ 549 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 20}}, 550 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 551 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(3, nil), MinTime: 40, MaxTime: 60}}, 552 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(4, nil), MinTime: 60, MaxTime: 80}}, 553 }, 554 noCompactMarks: map[ulid.ULID]*metadata.NoCompactMark{ 555 ulid.MustNew(1, nil): {}, 556 ulid.MustNew(2, nil): {}, 557 ulid.MustNew(3, nil): {}, 558 ulid.MustNew(4, nil): {}, 559 }, 560 }, 561 { 562 name: `Block for the next parent range appeared, and we have a gap with size 20 between second and third block. 563 Second block is excluded.`, 564 metas: []*metadata.Meta{ 565 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 20}}, 566 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 567 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(4, nil), MinTime: 60, MaxTime: 80}}, 568 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(5, nil), MinTime: 80, MaxTime: 100}}, 569 }, 570 noCompactMarks: map[ulid.ULID]*metadata.NoCompactMark{ 571 ulid.MustNew(2, nil): {}, 572 }, 573 }, 574 { 575 name: "We have 20, 60, 20, 60, 240 range blocks. We could compact 20 + 60 + 60, but sixth 6th is excluded", 576 metas: []*metadata.Meta{ 577 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 578 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(4, nil), MinTime: 60, MaxTime: 120}}, 579 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(5, nil), MinTime: 960, MaxTime: 980}}, // Fresh one. 580 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(6, nil), MinTime: 120, MaxTime: 180}}, 581 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(7, nil), MinTime: 720, MaxTime: 960}}, 582 }, 583 noCompactMarks: map[ulid.ULID]*metadata.NoCompactMark{ 584 ulid.MustNew(6, nil): {}, 585 }, 586 expected: []*metadata.Meta{ 587 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 588 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(4, nil), MinTime: 60, MaxTime: 120}}, 589 }, 590 }, 591 { 592 name: "We have 20, 60, 20, 60, 240 range blocks. We could compact 20 + 60 + 60, but 4th is excluded", 593 metas: []*metadata.Meta{ 594 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 595 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(4, nil), MinTime: 60, MaxTime: 120}}, 596 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(5, nil), MinTime: 960, MaxTime: 980}}, // Fresh one. 597 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(6, nil), MinTime: 120, MaxTime: 180}}, 598 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(7, nil), MinTime: 720, MaxTime: 960}}, 599 }, 600 noCompactMarks: map[ulid.ULID]*metadata.NoCompactMark{ 601 ulid.MustNew(4, nil): {}, 602 }, 603 }, 604 { 605 name: "Do not select large blocks that have many tombstones when fresh appears but are excluded", 606 metas: []*metadata.Meta{ 607 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 540, Stats: tsdb.BlockStats{ 608 NumSeries: 10, 609 NumTombstones: 3, 610 }}}, 611 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 540, MaxTime: 560}}, 612 }, 613 noCompactMarks: map[ulid.ULID]*metadata.NoCompactMark{ 614 ulid.MustNew(1, nil): {}, 615 }, 616 }, 617 // |--------------| 618 // |----------------| 619 // |--------------| 620 { 621 name: "Overlapping blocks 1, but one is excluded", 622 metas: []*metadata.Meta{ 623 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 20}}, 624 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 19, MaxTime: 40}}, 625 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(3, nil), MinTime: 40, MaxTime: 60}}, 626 }, 627 noCompactMarks: map[ulid.ULID]*metadata.NoCompactMark{ 628 ulid.MustNew(1, nil): {}, 629 }, 630 }, 631 } { 632 t.Run(c.name, func(t *testing.T) { 633 metasByMinTime := make([]*metadata.Meta, len(c.metas)) 634 for i := range metasByMinTime { 635 metasByMinTime[i] = c.metas[i] 636 } 637 sort.Slice(metasByMinTime, func(i, j int) bool { 638 return metasByMinTime[i].MinTime < metasByMinTime[j].MinTime 639 }) 640 g.noCompactMarkedMap = c.noCompactMarks 641 plan, err := tsdbBasedPlanner.Plan(context.Background(), metasByMinTime, nil, nil) 642 testutil.Ok(t, err) 643 testutil.Equals(t, c.expected, plan) 644 }) 645 } 646 } 647 648 func TestLargeTotalIndexSizeFilter_Plan(t *testing.T) { 649 ranges := []int64{ 650 20, 651 60, 652 180, 653 540, 654 1620, 655 } 656 657 bkt := objstore.NewInMemBucket() 658 g := &GatherNoCompactionMarkFilter{} 659 660 marked := promauto.With(nil).NewCounter(prometheus.CounterOpts{}) 661 planner := WithLargeTotalIndexSizeFilter(NewPlanner(log.NewNopLogger(), ranges, g), bkt, 100, marked) 662 var lastMarkValue float64 663 for _, c := range []struct { 664 name string 665 metas []*metadata.Meta 666 667 expected []*metadata.Meta 668 expectedMarks float64 669 }{ 670 { 671 name: "Outside range and excluded", 672 metas: []*metadata.Meta{ 673 {Thanos: metadata.Thanos{Files: []metadata.File{{RelPath: block.IndexFilename, SizeBytes: 100}}}, 674 BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 20}}, 675 }, 676 expectedMarks: 0, 677 }, 678 { 679 name: "Blocks to fill the entire parent, but with first one too large.", 680 metas: []*metadata.Meta{ 681 {Thanos: metadata.Thanos{Files: []metadata.File{{RelPath: block.IndexFilename, SizeBytes: 41}}}, 682 BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 20}}, 683 {Thanos: metadata.Thanos{Files: []metadata.File{{RelPath: block.IndexFilename, SizeBytes: 30}}}, 684 BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 685 {Thanos: metadata.Thanos{Files: []metadata.File{{RelPath: block.IndexFilename, SizeBytes: 30}}}, 686 BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(3, nil), MinTime: 40, MaxTime: 60}}, 687 {Thanos: metadata.Thanos{Files: []metadata.File{{RelPath: block.IndexFilename, SizeBytes: 30}}}, 688 BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(4, nil), MinTime: 60, MaxTime: 80}}, 689 }, 690 expectedMarks: 1, 691 expected: []*metadata.Meta{ 692 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 693 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(3, nil), MinTime: 40, MaxTime: 60}}, 694 }, 695 }, 696 { 697 name: "Blocks to fill the entire parent, but with second one too large.", 698 metas: []*metadata.Meta{ 699 {Thanos: metadata.Thanos{Files: []metadata.File{{RelPath: block.IndexFilename, SizeBytes: 30}}}, 700 BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 20}}, 701 {Thanos: metadata.Thanos{Files: []metadata.File{{RelPath: block.IndexFilename, SizeBytes: 41}}}, 702 BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 703 {Thanos: metadata.Thanos{Files: []metadata.File{{RelPath: block.IndexFilename, SizeBytes: 30}}}, 704 BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(3, nil), MinTime: 40, MaxTime: 60}}, 705 {Thanos: metadata.Thanos{Files: []metadata.File{{RelPath: block.IndexFilename, SizeBytes: 20}}}, 706 BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(4, nil), MinTime: 60, MaxTime: 80}}, 707 }, 708 expectedMarks: 1, 709 }, 710 { 711 name: "Blocks to fill the entire parent, but with last size exceeded (should not matter and not even marked).", 712 metas: []*metadata.Meta{ 713 {Thanos: metadata.Thanos{Files: []metadata.File{{RelPath: block.IndexFilename, SizeBytes: 10}}}, 714 BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 20}}, 715 {Thanos: metadata.Thanos{Files: []metadata.File{{RelPath: block.IndexFilename, SizeBytes: 10}}}, 716 BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 717 {Thanos: metadata.Thanos{Files: []metadata.File{{RelPath: block.IndexFilename, SizeBytes: 10}}}, 718 BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(3, nil), MinTime: 40, MaxTime: 60}}, 719 {Thanos: metadata.Thanos{Files: []metadata.File{{RelPath: block.IndexFilename, SizeBytes: 90}}}, 720 BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(4, nil), MinTime: 60, MaxTime: 80}}, 721 }, 722 expected: []*metadata.Meta{ 723 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 20}}, 724 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 725 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(3, nil), MinTime: 40, MaxTime: 60}}, 726 }, 727 }, 728 { 729 name: "Blocks to fill the entire parent, but with pre-last one and first too large.", 730 metas: []*metadata.Meta{ 731 {Thanos: metadata.Thanos{Files: []metadata.File{{RelPath: block.IndexFilename, SizeBytes: 90}}}, 732 BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 20}}, 733 {Thanos: metadata.Thanos{Files: []metadata.File{{RelPath: block.IndexFilename, SizeBytes: 30}}}, 734 BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 735 {Thanos: metadata.Thanos{Files: []metadata.File{{RelPath: block.IndexFilename, SizeBytes: 30}}}, 736 BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(3, nil), MinTime: 40, MaxTime: 50}}, 737 {Thanos: metadata.Thanos{Files: []metadata.File{{RelPath: block.IndexFilename, SizeBytes: 90}}}, 738 BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(4, nil), MinTime: 50, MaxTime: 60}}, 739 {Thanos: metadata.Thanos{Files: []metadata.File{{RelPath: block.IndexFilename, SizeBytes: 90}}}, 740 BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(5, nil), MinTime: 60, MaxTime: 80}}, 741 }, 742 expected: []*metadata.Meta{ 743 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 744 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(3, nil), MinTime: 40, MaxTime: 50}}, 745 }, 746 expectedMarks: 2, 747 }, 748 { 749 name: `Block for the next parent range appeared, and we have a gap with size 20 between second and third block. 750 Second block is excluded.`, 751 metas: []*metadata.Meta{ 752 {Thanos: metadata.Thanos{Files: []metadata.File{{RelPath: block.IndexFilename, SizeBytes: 30}}}, 753 BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 20}}, 754 {Thanos: metadata.Thanos{Files: []metadata.File{{RelPath: block.IndexFilename, SizeBytes: 90}}}, 755 BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 756 {Thanos: metadata.Thanos{Files: []metadata.File{{RelPath: block.IndexFilename, SizeBytes: 30}}}, 757 BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(4, nil), MinTime: 60, MaxTime: 80}}, 758 {Thanos: metadata.Thanos{Files: []metadata.File{{RelPath: block.IndexFilename, SizeBytes: 30}}}, 759 BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(5, nil), MinTime: 80, MaxTime: 100}}, 760 }, 761 expectedMarks: 1, 762 }, 763 { 764 name: "We have 20, 60, 20, 60, 240 range blocks. We could compact 20 + 60 + 60, but sixth 6th is excluded", 765 metas: []*metadata.Meta{ 766 {Thanos: metadata.Thanos{Files: []metadata.File{{RelPath: block.IndexFilename, SizeBytes: 30}}}, 767 BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 768 {Thanos: metadata.Thanos{Files: []metadata.File{{RelPath: block.IndexFilename, SizeBytes: 30}}}, 769 BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(4, nil), MinTime: 60, MaxTime: 120}}, 770 {Thanos: metadata.Thanos{Files: []metadata.File{{RelPath: block.IndexFilename, SizeBytes: 30}}}, 771 BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(5, nil), MinTime: 960, MaxTime: 980}}, // Fresh one. 772 {Thanos: metadata.Thanos{Files: []metadata.File{{RelPath: block.IndexFilename, SizeBytes: 90}}}, 773 BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(6, nil), MinTime: 120, MaxTime: 180}}, 774 {Thanos: metadata.Thanos{Files: []metadata.File{{RelPath: block.IndexFilename, SizeBytes: 30}}}, 775 BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(7, nil), MinTime: 720, MaxTime: 960}}, 776 }, 777 expected: []*metadata.Meta{ 778 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 20, MaxTime: 40}}, 779 {BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(4, nil), MinTime: 60, MaxTime: 120}}, 780 }, 781 expectedMarks: 1, 782 }, 783 // |--------------| 784 // |----------------| 785 // |--------------| 786 { 787 name: "Overlapping blocks 1, but total is too large", 788 metas: []*metadata.Meta{ 789 {Thanos: metadata.Thanos{Files: []metadata.File{{RelPath: block.IndexFilename, SizeBytes: 90}}}, 790 BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(1, nil), MinTime: 0, MaxTime: 20}}, 791 {Thanos: metadata.Thanos{Files: []metadata.File{{RelPath: block.IndexFilename, SizeBytes: 30}}}, 792 BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(2, nil), MinTime: 19, MaxTime: 40}}, 793 {Thanos: metadata.Thanos{Files: []metadata.File{{RelPath: block.IndexFilename, SizeBytes: 30}}}, 794 BlockMeta: tsdb.BlockMeta{Version: 1, ULID: ulid.MustNew(3, nil), MinTime: 40, MaxTime: 60}}, 795 }, 796 expectedMarks: 1, 797 }, 798 } { 799 if !t.Run(c.name, func(t *testing.T) { 800 t.Run("from meta", func(t *testing.T) { 801 obj := bkt.Objects() 802 for o := range obj { 803 testutil.Ok(t, bkt.Delete(context.Background(), o)) 804 } 805 806 metasByMinTime := make([]*metadata.Meta, len(c.metas)) 807 for i := range metasByMinTime { 808 orig := c.metas[i] 809 m := &metadata.Meta{} 810 *m = *orig 811 metasByMinTime[i] = m 812 } 813 sort.Slice(metasByMinTime, func(i, j int) bool { 814 return metasByMinTime[i].MinTime < metasByMinTime[j].MinTime 815 }) 816 817 plan, err := planner.Plan(context.Background(), metasByMinTime, nil, nil) 818 testutil.Ok(t, err) 819 820 for _, m := range plan { 821 // For less boilerplate. 822 m.Thanos = metadata.Thanos{} 823 } 824 testutil.Equals(t, c.expected, plan) 825 testutil.Equals(t, c.expectedMarks, promtest.ToFloat64(marked)-lastMarkValue) 826 lastMarkValue = promtest.ToFloat64(marked) 827 }) 828 t.Run("from bkt", func(t *testing.T) { 829 obj := bkt.Objects() 830 for o := range obj { 831 testutil.Ok(t, bkt.Delete(context.Background(), o)) 832 } 833 834 metasByMinTime := make([]*metadata.Meta, len(c.metas)) 835 for i := range metasByMinTime { 836 orig := c.metas[i] 837 m := &metadata.Meta{} 838 *m = *orig 839 metasByMinTime[i] = m 840 } 841 sort.Slice(metasByMinTime, func(i, j int) bool { 842 return metasByMinTime[i].MinTime < metasByMinTime[j].MinTime 843 }) 844 845 for _, m := range metasByMinTime { 846 testutil.Ok(t, bkt.Upload(context.Background(), filepath.Join(m.ULID.String(), block.IndexFilename), bytes.NewReader(make([]byte, m.Thanos.Files[0].SizeBytes)))) 847 m.Thanos = metadata.Thanos{} 848 } 849 850 plan, err := planner.Plan(context.Background(), metasByMinTime, nil, nil) 851 testutil.Ok(t, err) 852 testutil.Equals(t, c.expected, plan) 853 testutil.Equals(t, c.expectedMarks, promtest.ToFloat64(marked)-lastMarkValue) 854 855 lastMarkValue = promtest.ToFloat64(marked) 856 }) 857 858 }) { 859 return 860 } 861 } 862 }