github.com/grafana/pyroscope@v1.18.0/pkg/metastore/index_raft_handler.go (about)

     1  package metastore
     2  
     3  import (
     4  	"context"
     5  	"time"
     6  
     7  	"github.com/go-kit/log"
     8  	"github.com/go-kit/log/level"
     9  	"github.com/hashicorp/raft"
    10  	"github.com/opentracing/opentracing-go/ext"
    11  	"github.com/pkg/errors"
    12  	"go.etcd.io/bbolt"
    13  
    14  	metastorev1 "github.com/grafana/pyroscope/api/gen/proto/go/metastore/v1"
    15  	"github.com/grafana/pyroscope/api/gen/proto/go/metastore/v1/raft_log"
    16  	"github.com/grafana/pyroscope/pkg/metastore/compaction"
    17  	"github.com/grafana/pyroscope/pkg/metastore/index"
    18  	indexstore "github.com/grafana/pyroscope/pkg/metastore/index/store"
    19  	"github.com/grafana/pyroscope/pkg/metastore/tracing"
    20  )
    21  
    22  type IndexInserter interface {
    23  	InsertBlock(*bbolt.Tx, *metastorev1.BlockMeta) error
    24  }
    25  
    26  type IndexDeleter interface {
    27  	DeleteShard(tx *bbolt.Tx, partition indexstore.Partition, tenant string, shard uint32) error
    28  }
    29  
    30  type IndexWriter interface {
    31  	IndexInserter
    32  	IndexDeleter
    33  }
    34  
    35  type Tombstones interface {
    36  	AddTombstones(*bbolt.Tx, *raft.Log, *metastorev1.Tombstones) error
    37  	DeleteTombstones(*bbolt.Tx, *raft.Log, ...*metastorev1.Tombstones) error
    38  	Exists(tenant string, shard uint32, block string) bool
    39  }
    40  
    41  type IndexCommandHandler struct {
    42  	logger     log.Logger
    43  	index      IndexWriter
    44  	tombstones Tombstones
    45  	compactor  compaction.Compactor
    46  }
    47  
    48  func NewIndexCommandHandler(
    49  	logger log.Logger,
    50  	index IndexWriter,
    51  	tombstones Tombstones,
    52  	compactor compaction.Compactor,
    53  ) *IndexCommandHandler {
    54  	return &IndexCommandHandler{
    55  		logger:     logger,
    56  		index:      index,
    57  		tombstones: tombstones,
    58  		compactor:  compactor,
    59  	}
    60  }
    61  
    62  func (m *IndexCommandHandler) AddBlock(ctx context.Context, tx *bbolt.Tx, cmd *raft.Log, req *metastorev1.AddBlockRequest) (resp *metastorev1.AddBlockResponse, err error) {
    63  	span, ctx := tracing.StartSpanFromContext(ctx, "raft.AddBlockMetadata")
    64  	span.SetTag("block_id", req.Block.GetId())
    65  	span.SetTag("shard", req.Block.GetShard())
    66  	span.SetTag("compaction_level", req.Block.GetCompactionLevel())
    67  	span.SetTag("raft_log_index", cmd.Index)
    68  	span.SetTag("raft_log_term", cmd.Term)
    69  	defer func() {
    70  		if err != nil {
    71  			ext.LogError(span, err)
    72  		}
    73  		span.Finish()
    74  	}()
    75  
    76  	e := compaction.NewBlockEntry(cmd, req.Block)
    77  	if m.tombstones.Exists(e.Tenant, e.Shard, e.ID) {
    78  		level.Warn(m.logger).Log("msg", "block already added and compacted", "block", e.ID)
    79  		return new(metastorev1.AddBlockResponse), nil
    80  	}
    81  
    82  	insertSpan, _ := tracing.StartSpanFromContext(ctx, "index.InsertBlock")
    83  	if err = m.index.InsertBlock(tx, req.Block); err != nil {
    84  		if errors.Is(err, index.ErrBlockExists) {
    85  			level.Warn(m.logger).Log("msg", "block already added", "block", e.ID)
    86  			return new(metastorev1.AddBlockResponse), nil
    87  		}
    88  		ext.LogError(insertSpan, err)
    89  		insertSpan.Finish()
    90  		level.Error(m.logger).Log("msg", "failed to add block to index", "block", e.ID, "err", err)
    91  		return nil, err
    92  	}
    93  	insertSpan.Finish()
    94  
    95  	compactSpan, _ := tracing.StartSpanFromContext(ctx, "compactor.Compact")
    96  	defer compactSpan.Finish()
    97  	if err = m.compactor.Compact(tx, e); err != nil {
    98  		level.Error(m.logger).Log("msg", "failed to add block to compaction", "block", e.ID, "err", err)
    99  		ext.LogError(compactSpan, err)
   100  		return nil, err
   101  	}
   102  	return new(metastorev1.AddBlockResponse), nil
   103  }
   104  
   105  func (m *IndexCommandHandler) TruncateIndex(ctx context.Context, tx *bbolt.Tx, cmd *raft.Log, req *raft_log.TruncateIndexRequest) (resp *raft_log.TruncateIndexResponse, err error) {
   106  	span, _ := tracing.StartSpanFromContext(ctx, "raft.TruncateIndex")
   107  	span.SetTag("tombstone_count", len(req.Tombstones))
   108  	span.SetTag("raft_log_index", cmd.Index)
   109  	span.SetTag("raft_log_term", cmd.Term)
   110  	span.SetTag("request_term", req.Term)
   111  	defer func() {
   112  		if err != nil {
   113  			ext.LogError(span, err)
   114  		}
   115  		span.Finish()
   116  	}()
   117  
   118  	if req.Term != cmd.Term {
   119  		level.Warn(m.logger).Log(
   120  			"msg", "rejecting index truncation request; term mismatch: leader has changed",
   121  			"current_term", cmd.Term,
   122  			"request_term", req.Term,
   123  		)
   124  		return new(raft_log.TruncateIndexResponse), nil
   125  	}
   126  	for _, tombstone := range req.Tombstones {
   127  		// Although it's not strictly necessary, we may pass any tombstones
   128  		// to TruncateIndex, and the Partition member may be missing.
   129  		if p := tombstone.Shard; p != nil {
   130  			pk := indexstore.Partition{
   131  				Timestamp: time.Unix(0, p.Timestamp),
   132  				Duration:  time.Duration(p.Duration),
   133  			}
   134  			if err = m.index.DeleteShard(tx, pk, p.Tenant, p.Shard); err != nil {
   135  				level.Error(m.logger).Log("msg", "failed to delete partition", "err", err)
   136  				return nil, err
   137  			}
   138  		}
   139  		if err = m.tombstones.AddTombstones(tx, cmd, tombstone); err != nil {
   140  			level.Error(m.logger).Log("msg", "failed to add partition tombstone", "err", err)
   141  			return nil, err
   142  		}
   143  	}
   144  	return new(raft_log.TruncateIndexResponse), nil
   145  }