github.com/grafana/pyroscope@v1.18.0/pkg/phlaredb/block/markers.go (about) 1 // SPDX-License-Identifier: AGPL-3.0-only 2 // Provenance-includes-location: https://github.com/thanos-io/thanos/blob/master/pkg/block/metadata/markers.go 3 // Provenance-includes-license: Apache-2.0 4 // Provenance-includes-copyright: The Thanos Authors. 5 6 package block 7 8 import ( 9 "context" 10 "encoding/json" 11 "io" 12 "path" 13 "time" 14 15 "github.com/go-kit/log" 16 "github.com/grafana/dskit/runutil" 17 "github.com/oklog/ulid/v2" 18 "github.com/pkg/errors" 19 20 "github.com/grafana/pyroscope/pkg/objstore" 21 ) 22 23 const ( 24 // DeletionMarkFilename is the known json filename for optional file storing details about when block is marked for deletion. 25 // If such file is present in block dir, it means the block is meant to be deleted after certain delay. 26 DeletionMarkFilename = "deletion-mark.json" 27 // NoCompactMarkFilename is the known json filename for optional file storing details about why block has to be excluded from compaction. 28 // If such file is present in block dir, it means the block has to excluded from compaction (both vertical and horizontal) or rewrite (e.g deletions). 29 NoCompactMarkFilename = "no-compact-mark.json" 30 31 // DeletionMarkVersion1 is the version of deletion-mark file supported by Thanos. 32 DeletionMarkVersion1 = 1 33 // NoCompactMarkVersion1 is the version of no-compact-mark file supported by Thanos. 34 NoCompactMarkVersion1 = 1 35 ) 36 37 var ( 38 // ErrorMarkerNotFound is the error when marker file is not found. 39 ErrorMarkerNotFound = errors.New("marker not found") 40 // ErrorUnmarshalMarker is the error when unmarshalling marker JSON file. 41 // This error can occur because marker has been partially uploaded to block storage 42 // or the marker file is not a valid json file. 43 ErrorUnmarshalMarker = errors.New("unmarshal marker JSON") 44 ) 45 46 type Marker interface { 47 markerFilename() string 48 } 49 50 // DeletionMark stores block id and when block was marked for deletion. 51 type DeletionMark struct { 52 // ID of the tsdb block. 53 ID ulid.ULID `json:"id"` 54 // Version of the file. 55 Version int `json:"version"` 56 // Details is a human readable string giving details of reason. 57 Details string `json:"details,omitempty"` 58 59 // DeletionTime is a unix timestamp of when the block was marked to be deleted. 60 DeletionTime int64 `json:"deletion_time"` 61 } 62 63 func (m *DeletionMark) markerFilename() string { return DeletionMarkFilename } 64 65 // NoCompactReason is a reason for a block to be excluded from compaction. 66 type NoCompactReason string 67 68 const ( 69 // ManualNoCompactReason is a custom reason of excluding from compaction that should be added when no-compact mark is added for unknown/user specified reason. 70 ManualNoCompactReason NoCompactReason = "manual" 71 // IndexSizeExceedingNoCompactReason is a reason of index being too big (for example exceeding 64GB limit: https://github.com/thanos-io/thanos/issues/1424) 72 // This reason can be ignored when vertical block sharding will be implemented. 73 IndexSizeExceedingNoCompactReason = "index-size-exceeding" 74 // OutOfOrderChunksNoCompactReason is a reason of to no compact block with index contains out of order chunk so that the compaction is not blocked. 75 OutOfOrderChunksNoCompactReason = "block-index-out-of-order-chunk" 76 ) 77 78 // NoCompactMark marker stores reason of block being excluded from compaction if needed. 79 type NoCompactMark struct { 80 // ID of the tsdb block. 81 ID ulid.ULID `json:"id"` 82 // Version of the file. 83 Version int `json:"version"` 84 // Details is a human readable string giving details of reason. 85 Details string `json:"details,omitempty"` 86 87 // NoCompactTime is a unix timestamp of when the block was marked for no compact. 88 NoCompactTime int64 `json:"no_compact_time"` 89 Reason NoCompactReason `json:"reason"` 90 } 91 92 func (n *NoCompactMark) markerFilename() string { return NoCompactMarkFilename } 93 94 // ReadMarker reads the given mark file from <dir>/<marker filename>.json in bucket. 95 // ReadMarker has a one-minute timeout for completing the read against the bucket. 96 // This protects against operations that can take unbounded time. 97 func ReadMarker(ctx context.Context, logger log.Logger, bkt objstore.BucketReader, dir string, marker Marker) error { 98 ctx, cancel := context.WithTimeout(ctx, time.Minute) 99 defer cancel() 100 101 markerFile := path.Join(dir, marker.markerFilename()) 102 // todo(cyriltovena): we should use ReaderWithExpectedErrs(bkt.IsObjNotFoundErr) since it's expected to not find the marker file. 103 r, err := bkt.Get(ctx, markerFile) 104 if err != nil { 105 if bkt.IsObjNotFoundErr(err) { 106 return ErrorMarkerNotFound 107 } 108 return errors.Wrapf(err, "get file: %s", markerFile) 109 } 110 defer runutil.CloseWithLogOnErr(logger, r, "close bkt marker reader") 111 112 metaContent, err := io.ReadAll(r) 113 if err != nil { 114 return errors.Wrapf(err, "read file: %s", markerFile) 115 } 116 117 if err := json.Unmarshal(metaContent, marker); err != nil { 118 return errors.Wrapf(ErrorUnmarshalMarker, "file: %s; err: %v", markerFile, err.Error()) 119 } 120 switch marker.markerFilename() { 121 case NoCompactMarkFilename: 122 if version := marker.(*NoCompactMark).Version; version != NoCompactMarkVersion1 { 123 return errors.Errorf("unexpected no-compact-mark file version %d, expected %d", version, NoCompactMarkVersion1) 124 } 125 case DeletionMarkFilename: 126 if version := marker.(*DeletionMark).Version; version != DeletionMarkVersion1 { 127 return errors.Errorf("unexpected deletion-mark file version %d, expected %d", version, DeletionMarkVersion1) 128 } 129 } 130 return nil 131 }