github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/storage/stores/tsdb/index/chunk.go (about)

     1  package index
     2  
     3  import (
     4  	"sort"
     5  
     6  	"github.com/prometheus/common/model"
     7  )
     8  
     9  // Meta holds information about a chunk of data.
    10  type ChunkMeta struct {
    11  	Checksum uint32
    12  
    13  	MinTime, MaxTime int64
    14  
    15  	// Bytes stored, rounded to nearest KB
    16  	KB uint32
    17  
    18  	Entries uint32
    19  }
    20  
    21  func (c ChunkMeta) From() model.Time                 { return model.Time(c.MinTime) }
    22  func (c ChunkMeta) Through() model.Time              { return model.Time(c.MaxTime) }
    23  func (c ChunkMeta) Bounds() (model.Time, model.Time) { return c.From(), c.Through() }
    24  
    25  type ChunkMetas []ChunkMeta
    26  
    27  func (c ChunkMetas) Len() int      { return len(c) }
    28  func (c ChunkMetas) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
    29  func (c ChunkMetas) Bounds() (mint, maxt model.Time) {
    30  	ln := len(c)
    31  	if ln == 0 {
    32  		return
    33  	}
    34  
    35  	mint, maxt = model.Time(c[0].MinTime), model.Time(c[ln-1].MaxTime)
    36  	// even when sorted, we need to check all chunks for maxt
    37  	// since we sort by (min, max, checksum). Therefore
    38  	// check mint here as well to ensure this works on unordered ChunkMetas too
    39  	for _, chk := range c {
    40  		from, through := chk.Bounds()
    41  		if mint > from {
    42  			mint = from
    43  		}
    44  
    45  		if maxt < through {
    46  			maxt = through
    47  		}
    48  	}
    49  	return
    50  }
    51  
    52  // Sort by (MinTime, MaxTime, Checksum)
    53  func (c ChunkMetas) Less(i, j int) bool {
    54  	a, b := c[i], c[j]
    55  	if a.MinTime != b.MinTime {
    56  		return a.MinTime < b.MinTime
    57  	}
    58  
    59  	if a.MaxTime != b.MaxTime {
    60  		return a.MaxTime < b.MaxTime
    61  	}
    62  
    63  	return a.Checksum < b.Checksum
    64  }
    65  
    66  // Finalize sorts and dedupes
    67  // TODO(owen-d): can we remove the need for this by ensuring we only push
    68  // in order and without duplicates?
    69  func (c ChunkMetas) Finalize() ChunkMetas {
    70  	sort.Sort(c)
    71  
    72  	if len(c) == 0 {
    73  		return c
    74  	}
    75  
    76  	res := ChunkMetasPool.Get()
    77  	lastDuplicate := -1
    78  	prior := c[0]
    79  
    80  	// minimize reslicing costs due to duplicates
    81  	for i := 1; i < len(c); i++ {
    82  		x := c[i]
    83  		if x.MinTime == prior.MinTime && x.MaxTime == prior.MaxTime && x.Checksum == prior.Checksum {
    84  			res = append(res, c[lastDuplicate+1:i]...)
    85  			lastDuplicate = i
    86  		}
    87  		prior = x
    88  	}
    89  
    90  	// no duplicates were found, short circuit
    91  	// by returning unmodified underlying slice
    92  	if len(res) == 0 {
    93  		ChunkMetasPool.Put(res) // release unused slice to pool
    94  		return c
    95  	}
    96  
    97  	// otherwise, append any remaining values
    98  	res = append(res, c[lastDuplicate+1:]...)
    99  	// release self to pool; res will be returned instead
   100  	ChunkMetasPool.Put(c)
   101  	return res
   102  
   103  }
   104  
   105  // Add adds ChunkMeta at the right place in order. It assumes existing ChunkMetas have already been sorted by using Finalize.
   106  // There is no chance of a data loss even if the chunks are not sorted because the chunk would anyways be added so the assumption is relatively safe.
   107  func (c ChunkMetas) Add(chk ChunkMeta) ChunkMetas {
   108  	j := sort.Search(len(c), func(i int) bool {
   109  		ichk := c[i]
   110  		if ichk.MinTime != chk.MinTime {
   111  			return ichk.MinTime > chk.MinTime
   112  		}
   113  
   114  		if ichk.MaxTime != chk.MaxTime {
   115  			return ichk.MaxTime > chk.MaxTime
   116  		}
   117  
   118  		return ichk.Checksum >= chk.Checksum
   119  	})
   120  
   121  	if j < len(c) && c[j].MinTime == chk.MinTime && c[j].MaxTime == chk.MaxTime && c[j].Checksum == chk.Checksum {
   122  		return c
   123  	}
   124  
   125  	res := append(c, chk)
   126  	copy(res[j+1:], res[j:])
   127  	res[j] = chk
   128  
   129  	return res
   130  }
   131  
   132  // Drop drops ChunkMeta. It assumes existing ChunkMetas have already been sorted by using Finalize.
   133  // Calling Drop on non-sorted result can result in not dropping the chunk even if it exists.
   134  // It returns a boolean indicating if the chunk was found and dropped or not so the caller can take appropriate actions if not.
   135  // ToDo(Sandeep): See if we can do something about the assumption on sorted chunks.
   136  // Maybe always build ChunkMetas using Add method which should always keep the ChunkMetas deduped and sorted.
   137  func (c ChunkMetas) Drop(chk ChunkMeta) (ChunkMetas, bool) {
   138  	j := sort.Search(len(c), func(i int) bool {
   139  		ichk := c[i]
   140  		if ichk.MinTime != chk.MinTime {
   141  			return ichk.MinTime >= chk.MinTime
   142  		}
   143  
   144  		if ichk.MaxTime != chk.MaxTime {
   145  			return ichk.MaxTime >= chk.MaxTime
   146  		}
   147  
   148  		return ichk.Checksum >= chk.Checksum
   149  	})
   150  
   151  	if j >= len(c) || c[j] != chk {
   152  		return c, false
   153  	}
   154  
   155  	return c[:j+copy(c[j:], c[j+1:])], true
   156  }