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  }