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 }