github.com/muhammadn/cortex@v1.9.1-0.20220510110439-46bb7000d03d/pkg/storage/tsdb/bucketindex/index.go (about)

     1  package bucketindex
     2  
     3  import (
     4  	"fmt"
     5  	"path/filepath"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/oklog/ulid"
    10  	"github.com/prometheus/prometheus/tsdb"
    11  	"github.com/thanos-io/thanos/pkg/block"
    12  	"github.com/thanos-io/thanos/pkg/block/metadata"
    13  
    14  	cortex_tsdb "github.com/cortexproject/cortex/pkg/storage/tsdb"
    15  	"github.com/cortexproject/cortex/pkg/util"
    16  )
    17  
    18  const (
    19  	IndexFilename           = "bucket-index.json"
    20  	IndexCompressedFilename = IndexFilename + ".gz"
    21  	IndexVersion1           = 1
    22  
    23  	SegmentsFormatUnknown = ""
    24  
    25  	// SegmentsFormat1Based6Digits defined segments numbered with 6 digits numbers in a sequence starting from number 1
    26  	// eg. (000001, 000002, 000003).
    27  	SegmentsFormat1Based6Digits = "1b6d"
    28  )
    29  
    30  // Index contains all known blocks and markers of a tenant.
    31  type Index struct {
    32  	// Version of the index format.
    33  	Version int `json:"version"`
    34  
    35  	// List of complete blocks (partial blocks are excluded from the index).
    36  	Blocks Blocks `json:"blocks"`
    37  
    38  	// List of block deletion marks.
    39  	BlockDeletionMarks BlockDeletionMarks `json:"block_deletion_marks"`
    40  
    41  	// UpdatedAt is a unix timestamp (seconds precision) of when the index has been updated
    42  	// (written in the storage) the last time.
    43  	UpdatedAt int64 `json:"updated_at"`
    44  }
    45  
    46  func (idx *Index) GetUpdatedAt() time.Time {
    47  	return time.Unix(idx.UpdatedAt, 0)
    48  }
    49  
    50  // RemoveBlock removes block and its deletion mark (if any) from index.
    51  func (idx *Index) RemoveBlock(id ulid.ULID) {
    52  	for i := 0; i < len(idx.Blocks); i++ {
    53  		if idx.Blocks[i].ID == id {
    54  			idx.Blocks = append(idx.Blocks[:i], idx.Blocks[i+1:]...)
    55  			break
    56  		}
    57  	}
    58  
    59  	for i := 0; i < len(idx.BlockDeletionMarks); i++ {
    60  		if idx.BlockDeletionMarks[i].ID == id {
    61  			idx.BlockDeletionMarks = append(idx.BlockDeletionMarks[:i], idx.BlockDeletionMarks[i+1:]...)
    62  			break
    63  		}
    64  	}
    65  }
    66  
    67  // Block holds the information about a block in the index.
    68  type Block struct {
    69  	// Block ID.
    70  	ID ulid.ULID `json:"block_id"`
    71  
    72  	// MinTime and MaxTime specify the time range all samples in the block are in (millis precision).
    73  	MinTime int64 `json:"min_time"`
    74  	MaxTime int64 `json:"max_time"`
    75  
    76  	// SegmentsFormat and SegmentsNum stores the format and number of chunks segments
    77  	// in the block, if they match a known pattern. We don't store the full segments
    78  	// files list in order to keep the index small. SegmentsFormat is empty if segments
    79  	// are unknown or don't match a known format.
    80  	SegmentsFormat string `json:"segments_format,omitempty"`
    81  	SegmentsNum    int    `json:"segments_num,omitempty"`
    82  
    83  	// UploadedAt is a unix timestamp (seconds precision) of when the block has been completed to be uploaded
    84  	// to the storage.
    85  	UploadedAt int64 `json:"uploaded_at"`
    86  }
    87  
    88  // Within returns whether the block contains samples within the provided range.
    89  // Input minT and maxT are both inclusive.
    90  func (m *Block) Within(minT, maxT int64) bool {
    91  	// NOTE: Block intervals are half-open: [MinTime, MaxTime).
    92  	return m.MinTime <= maxT && minT < m.MaxTime
    93  }
    94  
    95  func (m *Block) GetUploadedAt() time.Time {
    96  	return time.Unix(m.UploadedAt, 0)
    97  }
    98  
    99  // ThanosMeta returns a block meta based on the known information in the index.
   100  // The returned meta doesn't include all original meta.json data but only a subset
   101  // of it.
   102  func (m *Block) ThanosMeta(userID string) *metadata.Meta {
   103  	return &metadata.Meta{
   104  		BlockMeta: tsdb.BlockMeta{
   105  			ULID:    m.ID,
   106  			MinTime: m.MinTime,
   107  			MaxTime: m.MaxTime,
   108  			Version: metadata.TSDBVersion1,
   109  		},
   110  		Thanos: metadata.Thanos{
   111  			Version: metadata.ThanosVersion1,
   112  			Labels: map[string]string{
   113  				cortex_tsdb.TenantIDExternalLabel: userID,
   114  			},
   115  			SegmentFiles: m.thanosMetaSegmentFiles(),
   116  		},
   117  	}
   118  }
   119  
   120  func (m *Block) thanosMetaSegmentFiles() (files []string) {
   121  	if m.SegmentsFormat == SegmentsFormat1Based6Digits {
   122  		for i := 1; i <= m.SegmentsNum; i++ {
   123  			files = append(files, fmt.Sprintf("%06d", i))
   124  		}
   125  	}
   126  
   127  	return files
   128  }
   129  
   130  func (m *Block) String() string {
   131  	minT := util.TimeFromMillis(m.MinTime).UTC()
   132  	maxT := util.TimeFromMillis(m.MaxTime).UTC()
   133  
   134  	return fmt.Sprintf("%s (min time: %s max time: %s)", m.ID, minT.String(), maxT.String())
   135  }
   136  
   137  func BlockFromThanosMeta(meta metadata.Meta) *Block {
   138  	segmentsFormat, segmentsNum := detectBlockSegmentsFormat(meta)
   139  
   140  	return &Block{
   141  		ID:             meta.ULID,
   142  		MinTime:        meta.MinTime,
   143  		MaxTime:        meta.MaxTime,
   144  		SegmentsFormat: segmentsFormat,
   145  		SegmentsNum:    segmentsNum,
   146  	}
   147  }
   148  
   149  func detectBlockSegmentsFormat(meta metadata.Meta) (string, int) {
   150  	if num, ok := detectBlockSegmentsFormat1Based6Digits(meta); ok {
   151  		return SegmentsFormat1Based6Digits, num
   152  	}
   153  
   154  	return "", 0
   155  }
   156  
   157  func detectBlockSegmentsFormat1Based6Digits(meta metadata.Meta) (int, bool) {
   158  	// Check the (deprecated) SegmentFiles.
   159  	if len(meta.Thanos.SegmentFiles) > 0 {
   160  		for i, f := range meta.Thanos.SegmentFiles {
   161  			if fmt.Sprintf("%06d", i+1) != f {
   162  				return 0, false
   163  			}
   164  		}
   165  		return len(meta.Thanos.SegmentFiles), true
   166  	}
   167  
   168  	// Check the Files.
   169  	if len(meta.Thanos.Files) > 0 {
   170  		num := 0
   171  		for _, file := range meta.Thanos.Files {
   172  			if !strings.HasPrefix(file.RelPath, block.ChunksDirname+string(filepath.Separator)) {
   173  				continue
   174  			}
   175  			if fmt.Sprintf("%s%s%06d", block.ChunksDirname, string(filepath.Separator), num+1) != file.RelPath {
   176  				return 0, false
   177  			}
   178  			num++
   179  		}
   180  
   181  		if num > 0 {
   182  			return num, true
   183  		}
   184  	}
   185  
   186  	return 0, false
   187  }
   188  
   189  // BlockDeletionMark holds the information about a block's deletion mark in the index.
   190  type BlockDeletionMark struct {
   191  	// Block ID.
   192  	ID ulid.ULID `json:"block_id"`
   193  
   194  	// DeletionTime is a unix timestamp (seconds precision) of when the block was marked to be deleted.
   195  	DeletionTime int64 `json:"deletion_time"`
   196  }
   197  
   198  func (m *BlockDeletionMark) GetDeletionTime() time.Time {
   199  	return time.Unix(m.DeletionTime, 0)
   200  }
   201  
   202  // ThanosMeta returns the Thanos deletion mark.
   203  func (m *BlockDeletionMark) ThanosDeletionMark() *metadata.DeletionMark {
   204  	return &metadata.DeletionMark{
   205  		ID:           m.ID,
   206  		Version:      metadata.DeletionMarkVersion1,
   207  		DeletionTime: m.DeletionTime,
   208  	}
   209  }
   210  
   211  func BlockDeletionMarkFromThanosMarker(mark *metadata.DeletionMark) *BlockDeletionMark {
   212  	return &BlockDeletionMark{
   213  		ID:           mark.ID,
   214  		DeletionTime: mark.DeletionTime,
   215  	}
   216  }
   217  
   218  // BlockDeletionMarks holds a set of block deletion marks in the index. No ordering guaranteed.
   219  type BlockDeletionMarks []*BlockDeletionMark
   220  
   221  func (s BlockDeletionMarks) GetULIDs() []ulid.ULID {
   222  	ids := make([]ulid.ULID, len(s))
   223  	for i, m := range s {
   224  		ids[i] = m.ID
   225  	}
   226  	return ids
   227  }
   228  
   229  func (s BlockDeletionMarks) Clone() BlockDeletionMarks {
   230  	clone := make(BlockDeletionMarks, len(s))
   231  	for i, m := range s {
   232  		v := *m
   233  		clone[i] = &v
   234  	}
   235  	return clone
   236  }
   237  
   238  // Blocks holds a set of blocks in the index. No ordering guaranteed.
   239  type Blocks []*Block
   240  
   241  func (s Blocks) GetULIDs() []ulid.ULID {
   242  	ids := make([]ulid.ULID, len(s))
   243  	for i, m := range s {
   244  		ids[i] = m.ID
   245  	}
   246  	return ids
   247  }
   248  
   249  func (s Blocks) String() string {
   250  	b := strings.Builder{}
   251  
   252  	for idx, m := range s {
   253  		if idx > 0 {
   254  			b.WriteString(", ")
   255  		}
   256  		b.WriteString(m.String())
   257  	}
   258  
   259  	return b.String()
   260  }