| 277 | } |
| 278 | |
| 279 | func (c *LeveledCompactor) plan(dms []dirMeta) ([]string, error) { |
| 280 | if len(dms) == 0 { |
| 281 | return nil, nil |
| 282 | } |
| 283 | |
| 284 | slices.SortFunc(dms, func(a, b dirMeta) int { |
| 285 | switch { |
| 286 | case a.meta.MinTime < b.meta.MinTime: |
| 287 | return -1 |
| 288 | case a.meta.MinTime > b.meta.MinTime: |
| 289 | return 1 |
| 290 | default: |
| 291 | return 0 |
| 292 | } |
| 293 | }) |
| 294 | |
| 295 | res := c.selectOverlappingDirs(dms) |
| 296 | if len(res) > 0 { |
| 297 | return res, nil |
| 298 | } |
| 299 | // No overlapping blocks, do compaction the usual way. |
| 300 | // We do not include a recently created block with max(minTime), so the block which was just created from WAL. |
| 301 | // This gives users a window of a full block size to piece-wise backup new data without having to care about data overlap. |
| 302 | dms = dms[:len(dms)-1] |
| 303 | |
| 304 | for _, dm := range c.selectDirs(dms) { |
| 305 | res = append(res, dm.dir) |
| 306 | } |
| 307 | if len(res) > 0 { |
| 308 | return res, nil |
| 309 | } |
| 310 | |
| 311 | // Compact any blocks with big enough time range that have >5% tombstones. |
| 312 | for i := len(dms) - 1; i >= 0; i-- { |
| 313 | meta := dms[i].meta |
| 314 | if meta.MaxTime-meta.MinTime < c.ranges[len(c.ranges)/2] { |
| 315 | // If the block is entirely deleted, then we don't care about the block being big enough. |
| 316 | // TODO: This is assuming a single tombstone is for a distinct series, which might not be true. |
| 317 | if meta.Stats.NumTombstones > 0 && meta.Stats.NumTombstones >= meta.Stats.NumSeries { |
| 318 | return []string{dms[i].dir}, nil |
| 319 | } |
| 320 | break |
| 321 | } |
| 322 | if float64(meta.Stats.NumTombstones)/float64(meta.Stats.NumSeries+1) > 0.05 { |
| 323 | return []string{dms[i].dir}, nil |
| 324 | } |
| 325 | } |
| 326 | |
| 327 | return nil, nil |
| 328 | } |
| 329 | |
| 330 | // selectDirs returns the dir metas that should be compacted into a single new block. |
| 331 | // If only a single block range is configured, the result is always nil. |