Files
Joe Elliottandzeekay e94374166e [Tempo 3.0] Remove compactor and v2 block encoding code (#6273)
* o7 compactor

Signed-off-by: Joe Elliott <[email protected]>

* wip: o7 v2

Signed-off-by: Joe Elliott <[email protected]>

* test cleanup

Signed-off-by: Joe Elliott <[email protected]>

* remove compactor from jsonnet

Signed-off-by: Joe Elliott <[email protected]>

* changelog

Signed-off-by: Joe Elliott <[email protected]>

* gen manifest

Signed-off-by: Joe Elliott <[email protected]>

* config cleanup

Signed-off-by: Joe Elliott <[email protected]>

* docs

Signed-off-by: Joe Elliott <[email protected]>

* jsonnet

Signed-off-by: Joe Elliott <[email protected]>

* remove encoding

Signed-off-by: Joe Elliott <[email protected]>

* remove data encoding

Signed-off-by: Joe Elliott <[email protected]>

* lint and cleanup

Signed-off-by: Joe Elliott <[email protected]>

* unflake TestWorker\?

Signed-off-by: Joe Elliott <[email protected]>

---------

Signed-off-by: Joe Elliott <[email protected]>
2026-02-22 00:55:18 -08:00

256 lines
7.6 KiB
Go

package blockbuilder
import (
"context"
"time"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/google/uuid"
"github.com/grafana/tempo/modules/blockbuilder/util"
"github.com/grafana/tempo/modules/overrides"
"github.com/grafana/tempo/pkg/dataquality"
"github.com/grafana/tempo/pkg/livetraces"
"github.com/grafana/tempo/pkg/tempopb"
"github.com/grafana/tempo/tempodb"
"github.com/grafana/tempo/tempodb/backend"
"github.com/grafana/tempo/tempodb/encoding"
"github.com/grafana/tempo/tempodb/wal"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
)
var metricBlockBuilderFlushedBlocks = promauto.NewCounterVec(
prometheus.CounterOpts{
Namespace: "tempo",
Subsystem: "block_builder",
Name: "flushed_blocks",
}, []string{"tenant"},
)
type tenantStore struct {
tenantID string
idGenerator util.IDGenerator
cfg BlockConfig
startTime time.Time
cycleDuration time.Duration
slackDuration time.Duration
logger log.Logger
overrides Overrides
enc encoding.VersionedEncoding
wal *wal.WAL
noCompactBlockID *backend.UUID
liveTraces *livetraces.LiveTraces[[]byte]
}
func newTenantStore(tenantID string, partitionID, startOffset uint64, startTime time.Time, cycleDuration, slackDuration time.Duration, cfg BlockConfig, logger log.Logger, wal *wal.WAL, enc encoding.VersionedEncoding, o Overrides) (*tenantStore, error) {
cfg.BlockCfg.CreateWithNoCompactFlag = true // blockbuilder creates blocks with the nocompact flag set by default
s := &tenantStore{
tenantID: tenantID,
idGenerator: util.NewDeterministicIDGenerator(tenantID, partitionID, startOffset),
startTime: startTime,
cycleDuration: cycleDuration,
slackDuration: slackDuration,
cfg: cfg,
logger: logger,
overrides: o,
wal: wal,
enc: enc,
liveTraces: livetraces.New(func(b []byte) uint64 { return uint64(len(b)) }, 0, 0, tenantID), // passing 0s for max idle and live time b/c block builder doesn't cut idle traces
}
return s, nil
}
func (s *tenantStore) AppendTrace(traceID []byte, tr []byte, ts time.Time) error {
maxSz := s.overrides.MaxBytesPerTrace(s.tenantID)
if err := s.liveTraces.PushWithTimestampAndLimits(ts, traceID, tr, 0, uint64(maxSz)); err != nil {
// Record dropped spans due to trace too large
// We have to unmarhal to count the number of spans.
// TODO - There might be a better way
t := &tempopb.Trace{}
if err := t.Unmarshal(tr); err != nil {
return err
}
count := 0
for _, b := range t.ResourceSpans {
for _, ss := range b.ScopeSpans {
count += len(ss.Spans)
}
}
overrides.RecordDiscardedSpans(count, overrides.ReasonTraceTooLarge, s.tenantID)
}
return nil
}
func (s *tenantStore) Flush(ctx context.Context, r tempodb.Reader, w tempodb.Writer, c tempodb.Compactor) error {
ctx, span := tracer.Start(ctx, "tenantStore.Flush", trace.WithAttributes(attribute.String("tenant", s.tenantID)))
defer span.End()
if s.liveTraces.Len() == 0 {
span.AddEvent("no live_traces to flush")
// This can happen if the tenant instance was created but
// no live traces were successfully pushed. i.e. all exceeded max trace size.
return nil
}
blockID, existingBlocksToBeCompacted, err := s.determineBlockIDs(ctx, r)
if err != nil {
return err
}
// TODO - Check if the existing block can be reused and exit early
if len(existingBlocksToBeCompacted) > 0 {
for _, blockID := range existingBlocksToBeCompacted {
level.Warn(s.logger).Log(
"msg", "Marking existing block compacted",
"tenant", s.tenantID,
"blockid", blockID,
)
span.AddEvent("marking existing block compacted", trace.WithAttributes(attribute.String("block_id", blockID.String())))
if err := c.MarkBlockCompacted(s.tenantID, blockID); err != nil {
return err
}
}
}
// Initial meta for creating the block
meta := backend.NewBlockMeta(s.tenantID, (uuid.UUID)(blockID), s.enc.Version())
meta.DedicatedColumns = s.overrides.DedicatedColumns(s.tenantID)
meta.ReplicationFactor = 1
meta.TotalObjects = int64(s.liveTraces.Len())
var (
st = time.Now()
l = s.wal.LocalBackend()
reader = backend.NewReader(l)
writer = backend.NewWriter(l)
iter = newLiveTracesIter(s.liveTraces)
)
level.Info(s.logger).Log(
"msg", "Flushing block",
"tenant", s.tenantID,
"blockid", meta.BlockID,
"meta", meta,
)
newMeta, err := s.enc.CreateBlock(ctx, &s.cfg.BlockCfg, meta, iter, reader, writer)
if err != nil {
return err
}
// Update meta timestamps which couldn't be known until we unmarshaled
// all of the traces.
start, end := iter.MinMaxTimestamps()
newMeta.StartTime, newMeta.EndTime = s.adjustTimeRangeForSlack(time.Unix(0, int64(start)), time.Unix(0, int64(end)))
newBlock, err := s.enc.OpenBlock(newMeta, reader)
if err != nil {
return err
}
if err := w.WriteBlock(ctx, NewWriteableBlock(newBlock, reader, writer)); err != nil {
span.RecordError(err)
return err
}
s.noCompactBlockID = &newMeta.BlockID
span.AddEvent("wrote block to backend", trace.WithAttributes(attribute.String("block_id", newMeta.BlockID.String())))
metricBlockBuilderFlushedBlocks.WithLabelValues(s.tenantID).Inc()
if err := s.wal.LocalBackend().ClearBlock((uuid.UUID)(newMeta.BlockID), s.tenantID); err != nil {
return err
}
span.AddEvent("cleared block from wal", trace.WithAttributes(attribute.String("block_id", newMeta.BlockID.String())))
level.Info(s.logger).Log(
"msg", "Flushed block",
"tenant", s.tenantID,
"blockid", newMeta.BlockID,
"elapsed", time.Since(st),
"meta", newMeta,
)
return nil
}
func (s *tenantStore) AllowCompaction(ctx context.Context, w tempodb.Writer) error {
if s.noCompactBlockID == nil {
return nil // no block to allow compaction for
}
if err := w.DeleteNoCompactFlag(ctx, s.tenantID, *s.noCompactBlockID); err != nil {
return err
}
s.noCompactBlockID = nil
return nil
}
// Adjust the time range based on when the record was added to the partition, factoring in slack and cycle duration.
// Any span with a start or end time outside this range will be constrained to the valid limits.
func (s *tenantStore) adjustTimeRangeForSlack(start, end time.Time) (time.Time, time.Time) {
startOfRange := s.startTime.Add(-s.slackDuration)
endOfRange := s.startTime.Add(s.slackDuration + s.cycleDuration)
warn := false
if start.Before(startOfRange) {
warn = true
start = startOfRange
}
// clock skew, missconfiguration or simply data tampering
if start.After(endOfRange) {
warn = true
start = endOfRange
}
// this can happen with old spans added to Tempo
// setting it to start to not jump forward unexpectedly
if end.Before(start) {
warn = true
end = start
}
if end.After(endOfRange) {
warn = true
end = endOfRange
}
if warn {
dataquality.WarnBlockBuilderOutsideIngestionSlack(s.tenantID)
}
return start, end
}
func (s *tenantStore) determineBlockIDs(ctx context.Context, r tempodb.Reader) (nextID backend.UUID, existingBlocksToBeCompacted []backend.UUID, err error) {
for {
nextID = s.idGenerator.NewID()
meta, compactedMeta, err := r.BlockMeta(ctx, s.tenantID, nextID)
if err != nil {
return backend.UUID{}, nil, err
}
if compactedMeta != nil {
// This ID is already in use but does not need to be compacted again
continue
}
if meta != nil {
// This ID is already in use and needs to be compacted
existingBlocksToBeCompacted = append(existingBlocksToBeCompacted, nextID)
continue
}
// This block is available
return nextID, existingBlocksToBeCompacted, nil
}
}