mirror of
https://github.com/hanzoai/traces.git
synced 2026-10-30 18:20:41 +00:00
155 lines
5.4 KiB
Go
155 lines
5.4 KiB
Go
package ingester
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/grafana/tempo/pkg/traceql"
|
|
"go.uber.org/atomic"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/grafana/tempo/pkg/tempopb"
|
|
"github.com/grafana/tempo/tempodb/backend"
|
|
"github.com/grafana/tempo/tempodb/backend/local"
|
|
"github.com/grafana/tempo/tempodb/encoding"
|
|
"github.com/grafana/tempo/tempodb/encoding/common"
|
|
)
|
|
|
|
const nameFlushed = "flushed"
|
|
|
|
// LocalBlock is a block stored in a local storage. It can be searched and flushed to a remote backend, and
|
|
// permanently tracks the flushed time with a special file in the block
|
|
type LocalBlock struct {
|
|
common.BackendBlock
|
|
reader backend.Reader
|
|
writer backend.Writer
|
|
|
|
flushedTime atomic.Int64 // protecting flushedTime b/c it's accessed from the store on flush and from the ingester instance checking flush time
|
|
}
|
|
|
|
var (
|
|
_ common.Finder = (*LocalBlock)(nil)
|
|
_ common.Searcher = (*LocalBlock)(nil)
|
|
)
|
|
|
|
// NewLocalBlock creates a local block
|
|
func NewLocalBlock(ctx context.Context, existingBlock common.BackendBlock, l *local.Backend) *LocalBlock {
|
|
c := &LocalBlock{
|
|
BackendBlock: existingBlock,
|
|
reader: backend.NewReader(l),
|
|
writer: backend.NewWriter(l),
|
|
}
|
|
|
|
flushedBytes, err := c.reader.Read(ctx, nameFlushed, (uuid.UUID)(c.BlockMeta().BlockID), c.BlockMeta().TenantID, nil)
|
|
if err == nil {
|
|
flushedTime := time.Time{}
|
|
err = flushedTime.UnmarshalText(flushedBytes)
|
|
if err == nil {
|
|
c.flushedTime.Store(flushedTime.Unix())
|
|
}
|
|
}
|
|
|
|
return c
|
|
}
|
|
|
|
func (c *LocalBlock) FindTraceByID(ctx context.Context, id common.ID, opts common.SearchOptions) (*tempopb.TraceByIDResponse, error) {
|
|
ctx, span := tracer.Start(ctx, "LocalBlock.FindTraceByID")
|
|
defer span.End()
|
|
return c.BackendBlock.FindTraceByID(ctx, id, opts)
|
|
}
|
|
|
|
func (c *LocalBlock) Search(ctx context.Context, req *tempopb.SearchRequest, opts common.SearchOptions) (*tempopb.SearchResponse, error) {
|
|
ctx, span := tracer.Start(ctx, "LocalBlock.Search")
|
|
defer span.End()
|
|
return c.BackendBlock.Search(ctx, req, opts)
|
|
}
|
|
|
|
func (c *LocalBlock) SearchTags(ctx context.Context, scope traceql.AttributeScope, cb common.TagsCallback, mcb common.MetricsCallback, opts common.SearchOptions) error {
|
|
ctx, span := tracer.Start(ctx, "LocalBlock.SearchTags")
|
|
defer span.End()
|
|
return c.BackendBlock.SearchTags(ctx, scope, cb, mcb, opts)
|
|
}
|
|
|
|
func (c *LocalBlock) SearchTagValues(ctx context.Context, tag string, cb common.TagValuesCallback, mcb common.MetricsCallback, opts common.SearchOptions) error {
|
|
ctx, span := tracer.Start(ctx, "LocalBlock.SearchTagValues")
|
|
defer span.End()
|
|
return c.BackendBlock.SearchTagValues(ctx, tag, cb, mcb, opts)
|
|
}
|
|
|
|
func (c *LocalBlock) SearchTagValuesV2(ctx context.Context, tag traceql.Attribute, cb common.TagValuesCallbackV2, mcb common.MetricsCallback, opts common.SearchOptions) error {
|
|
ctx, span := tracer.Start(ctx, "LocalBlock.SearchTagValuesV2")
|
|
defer span.End()
|
|
return c.BackendBlock.SearchTagValuesV2(ctx, tag, cb, mcb, opts)
|
|
}
|
|
|
|
func (c *LocalBlock) Fetch(ctx context.Context, req traceql.FetchSpansRequest, opts common.SearchOptions) (traceql.FetchSpansResponse, error) {
|
|
ctx, span := tracer.Start(ctx, "LocalBlock.Fetch")
|
|
defer span.End()
|
|
return c.BackendBlock.Fetch(ctx, req, opts)
|
|
}
|
|
|
|
func (c *LocalBlock) FetchTagValues(ctx context.Context, req traceql.FetchTagValuesRequest, cb traceql.FetchTagValuesCallback, mcb common.MetricsCallback, opts common.SearchOptions) error {
|
|
ctx, span := tracer.Start(ctx, "LocalBlock.FetchTagValues")
|
|
defer span.End()
|
|
return c.BackendBlock.FetchTagValues(ctx, req, cb, mcb, opts)
|
|
}
|
|
|
|
func (c *LocalBlock) FetchTagNames(ctx context.Context, req traceql.FetchTagsRequest, cb traceql.FetchTagsCallback, mcb common.MetricsCallback, opts common.SearchOptions) error {
|
|
ctx, span := tracer.Start(ctx, "LocalBlock.FetchTagNames")
|
|
defer span.End()
|
|
return c.BackendBlock.FetchTagNames(ctx, req, cb, mcb, opts)
|
|
}
|
|
|
|
// FlushedTime returns the time the block was flushed. Will return 0
|
|
//
|
|
// if the block was never flushed
|
|
func (c *LocalBlock) FlushedTime() time.Time {
|
|
unixTime := c.flushedTime.Load()
|
|
if unixTime == 0 {
|
|
return time.Time{} // return 0 time. 0 unix time is jan 1, 1970
|
|
}
|
|
return time.Unix(unixTime, 0)
|
|
}
|
|
|
|
func (c *LocalBlock) SetFlushed(ctx context.Context) error {
|
|
flushedTime := time.Now()
|
|
flushedBytes, err := flushedTime.MarshalText()
|
|
if err != nil {
|
|
return fmt.Errorf("error marshalling flush time to text: %w", err)
|
|
}
|
|
|
|
err = c.writer.Write(ctx, nameFlushed, (uuid.UUID)(c.BlockMeta().BlockID), c.BlockMeta().TenantID, flushedBytes, nil)
|
|
if err != nil {
|
|
return fmt.Errorf("error writing ingester block flushed file: %w", err)
|
|
}
|
|
|
|
c.flushedTime.Store(flushedTime.Unix())
|
|
return nil
|
|
}
|
|
|
|
func (c *LocalBlock) Write(ctx context.Context, w backend.Writer) error {
|
|
err := encoding.CopyBlock(ctx, c.BlockMeta(), c.reader, w)
|
|
if err != nil {
|
|
return fmt.Errorf("error copying block from local to remote backend: %w", err)
|
|
}
|
|
|
|
err = c.SetFlushed(ctx)
|
|
return err
|
|
}
|
|
|
|
func (c *LocalBlock) SetDiskCache(ctx context.Context, cacheKey string, data []byte) error {
|
|
return c.writer.Write(ctx, cacheKey, (uuid.UUID)(c.BlockMeta().BlockID), c.BlockMeta().TenantID, data, nil)
|
|
}
|
|
|
|
func (c *LocalBlock) GetDiskCache(ctx context.Context, cacheKey string) ([]byte, error) {
|
|
data, err := c.reader.Read(ctx, cacheKey, (uuid.UUID)(c.BlockMeta().BlockID), c.BlockMeta().TenantID, nil)
|
|
if errors.Is(err, backend.ErrDoesNotExist) {
|
|
// file doesn't exist, so it's a cache miss
|
|
return nil, nil
|
|
}
|
|
|
|
return data, err
|
|
}
|