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 }