Files
traces/modules/querier/querier.go
T
Joe ElliottandGitHub d5b8266cf4 Governance/Repo cleanup (#225)
* First pass governance

Signed-off-by: Joe Elliott <number101010@gmail.com>

* Readme overhaul

Signed-off-by: Joe Elliott <number101010@gmail.com>

* added logo

Signed-off-by: Joe Elliott <number101010@gmail.com>

* added governance

Signed-off-by: Joe Elliott <number101010@gmail.com>

* Updated otel proto comment

Signed-off-by: Joe Elliott <number101010@gmail.com>

* removed shenaniganery

Signed-off-by: Joe Elliott <number101010@gmail.com>
2020-10-14 10:12:42 -04:00

206 lines
6.2 KiB
Go

package querier
import (
"context"
"fmt"
"github.com/gogo/protobuf/proto"
"github.com/opentracing/opentracing-go"
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/weaveworks/common/user"
"github.com/cortexproject/cortex/pkg/ring"
ring_client "github.com/cortexproject/cortex/pkg/ring/client"
"github.com/cortexproject/cortex/pkg/util"
"github.com/cortexproject/cortex/pkg/util/services"
ingester_client "github.com/grafana/tempo/modules/ingester/client"
"github.com/grafana/tempo/modules/overrides"
"github.com/grafana/tempo/modules/storage"
"github.com/grafana/tempo/pkg/tempopb"
tempo_util "github.com/grafana/tempo/pkg/util"
"github.com/grafana/tempo/pkg/validation"
)
var (
metricQueryReads = promauto.NewHistogramVec(prometheus.HistogramOpts{
Namespace: "tempo",
Name: "query_reads",
Help: "count of reads",
Buckets: prometheus.ExponentialBuckets(1, 2, 10),
}, []string{"layer"})
metricQueryBytesRead = promauto.NewHistogramVec(prometheus.HistogramOpts{
Namespace: "tempo",
Name: "query_bytes_read",
Help: "bytes read",
Buckets: prometheus.ExponentialBuckets(1024, 2, 10),
}, []string{"layer"})
metricIngesterClients = promauto.NewGauge(prometheus.GaugeOpts{
Namespace: "tempo",
Name: "querier_ingester_clients",
Help: "The current number of ingester clients.",
})
)
// Querier handlers queries.
type Querier struct {
services.Service
cfg Config
ring ring.ReadRing
pool *ring_client.Pool
store storage.Store
limits *overrides.Overrides
subservicesWatcher *services.FailureWatcher
}
type responseFromIngesters struct {
addr string
response interface{}
}
// New makes a new Querier.
func New(cfg Config, clientCfg ingester_client.Config, ring ring.ReadRing, store storage.Store, limits *overrides.Overrides) (*Querier, error) {
factory := func(addr string) (ring_client.PoolClient, error) {
return ingester_client.New(addr, clientCfg)
}
q := &Querier{
cfg: cfg,
ring: ring,
pool: ring_client.NewPool("querier_pool",
clientCfg.PoolConfig,
ring_client.NewRingServiceDiscovery(ring),
factory,
metricIngesterClients,
util.Logger),
store: store,
limits: limits,
}
q.subservicesWatcher = services.NewFailureWatcher()
q.subservicesWatcher.WatchService(q.pool)
q.Service = services.NewBasicService(q.starting, q.running, q.stopping)
return q, nil
}
func (q *Querier) starting(ctx context.Context) error {
err := services.StartAndAwaitRunning(ctx, q.pool)
if err != nil {
return fmt.Errorf("failed to start pool %w", err)
}
return nil
}
func (q *Querier) running(ctx context.Context) error {
select {
case <-ctx.Done():
return nil
case err := <-q.subservicesWatcher.Chan():
return fmt.Errorf("querier subservices failed %w", err)
}
}
// Called after distributor is asked to stop via StopAsync.
func (q *Querier) stopping(_ error) error {
return services.StopAndAwaitTerminated(context.Background(), q.pool)
}
// FindTraceByID implements tempopb.Querier.
func (q *Querier) FindTraceByID(ctx context.Context, req *tempopb.TraceByIDRequest) (*tempopb.TraceByIDResponse, error) {
if !validation.ValidTraceID(req.TraceID) {
return nil, fmt.Errorf("invalid trace id")
}
userID, err := user.ExtractOrgID(ctx)
if err != nil {
return nil, errors.Wrap(err, "error extracting org id in Querier.FindTraceByID")
}
span, ctx := opentracing.StartSpanFromContext(ctx, "Querier.FindTraceByID")
defer span.Finish()
key := tempo_util.TokenFor(userID, req.TraceID)
const maxExpectedReplicationSet = 3 // 3. b/c frigg it
var descs [maxExpectedReplicationSet]ring.IngesterDesc
replicationSet, err := q.ring.Get(key, ring.Read, descs[:0])
if err != nil {
return nil, errors.Wrap(err, "error finding ingesters in Querier.FindTraceByID")
}
// get responses from all ingesters in parallel
responses, err := q.forGivenIngesters(ctx, replicationSet, func(client tempopb.QuerierClient) (interface{}, error) {
return client.FindTraceByID(opentracing.ContextWithSpan(ctx, span), req)
})
if err != nil {
return nil, errors.Wrap(err, "error querying ingesters in Querier.FindTraceByID")
}
var completeTrace *tempopb.Trace
for _, r := range responses {
trace := r.response.(*tempopb.TraceByIDResponse).Trace
if trace != nil {
completeTrace = tempo_util.CombineTraceProtos(completeTrace, trace)
}
}
// if the ingester didn't have it check the store.
if completeTrace == nil {
foundBytes, metrics, err := q.store.Find(opentracing.ContextWithSpan(ctx, span), userID, req.TraceID)
if err != nil {
return nil, errors.Wrap(err, "error querying store in Querier.FindTraceByID")
}
out := &tempopb.Trace{}
err = proto.Unmarshal(foundBytes, out)
if err != nil {
return nil, err
}
completeTrace = out
metricQueryReads.WithLabelValues("bloom").Observe(float64(metrics.BloomFilterReads.Load()))
metricQueryBytesRead.WithLabelValues("bloom").Observe(float64(metrics.BloomFilterBytesRead.Load()))
metricQueryReads.WithLabelValues("index").Observe(float64(metrics.IndexReads.Load()))
metricQueryBytesRead.WithLabelValues("index").Observe(float64(metrics.IndexBytesRead.Load()))
metricQueryReads.WithLabelValues("block").Observe(float64(metrics.BlockReads.Load()))
metricQueryBytesRead.WithLabelValues("block").Observe(float64(metrics.BlockBytesRead.Load()))
}
return &tempopb.TraceByIDResponse{
Trace: completeTrace,
}, nil
}
// forGivenIngesters runs f, in parallel, for given ingesters
func (q *Querier) forGivenIngesters(ctx context.Context, replicationSet ring.ReplicationSet, f func(tempopb.QuerierClient) (interface{}, error)) ([]responseFromIngesters, error) {
results, err := replicationSet.Do(ctx, q.cfg.ExtraQueryDelay, func(ingester *ring.IngesterDesc) (interface{}, error) {
client, err := q.pool.GetClientFor(ingester.Addr)
if err != nil {
return nil, err
}
resp, err := f(client.(tempopb.QuerierClient))
if err != nil {
return nil, err
}
return responseFromIngesters{ingester.Addr, resp}, nil
})
if err != nil {
return nil, err
}
responses := make([]responseFromIngesters, 0, len(results))
for _, result := range results {
responses = append(responses, result.(responseFromIngesters))
}
return responses, err
}