github.com/thanos-io/thanos@v0.32.5/pkg/block/metadata/markers.go (about)

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