github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/storage/stores/shipper/indexgateway/gateway.go (about)

     1  package indexgateway
     2  
     3  import (
     4  	"context"
     5  	"sync"
     6  
     7  	"github.com/go-kit/log"
     8  	"github.com/grafana/dskit/services"
     9  	"github.com/grafana/dskit/tenant"
    10  	"github.com/prometheus/client_golang/prometheus"
    11  	"github.com/prometheus/common/model"
    12  	"github.com/prometheus/prometheus/model/labels"
    13  
    14  	"github.com/grafana/loki/pkg/logproto"
    15  	"github.com/grafana/loki/pkg/logql/syntax"
    16  	"github.com/grafana/loki/pkg/storage/chunk"
    17  	"github.com/grafana/loki/pkg/storage/chunk/fetcher"
    18  	"github.com/grafana/loki/pkg/storage/stores/index/stats"
    19  	"github.com/grafana/loki/pkg/storage/stores/series/index"
    20  	"github.com/grafana/loki/pkg/storage/stores/shipper/util"
    21  )
    22  
    23  const (
    24  	maxIndexEntriesPerResponse = 1000
    25  )
    26  
    27  type IndexQuerier interface {
    28  	GetChunkRefs(ctx context.Context, userID string, from, through model.Time, matchers ...*labels.Matcher) ([][]chunk.Chunk, []*fetcher.Fetcher, error)
    29  	GetSeries(ctx context.Context, userID string, from, through model.Time, matchers ...*labels.Matcher) ([]labels.Labels, error)
    30  	LabelValuesForMetricName(ctx context.Context, userID string, from, through model.Time, metricName string, labelName string, matchers ...*labels.Matcher) ([]string, error)
    31  	LabelNamesForMetricName(ctx context.Context, userID string, from, through model.Time, metricName string) ([]string, error)
    32  	Stats(ctx context.Context, userID string, from, through model.Time, matchers ...*labels.Matcher) (*stats.Stats, error)
    33  	Stop()
    34  }
    35  
    36  type IndexClient interface {
    37  	QueryPages(ctx context.Context, queries []index.Query, callback index.QueryPagesCallback) error
    38  	Stop()
    39  }
    40  
    41  type Gateway struct {
    42  	services.Service
    43  
    44  	indexQuerier IndexQuerier
    45  	indexClient  IndexClient
    46  
    47  	cfg Config
    48  	log log.Logger
    49  
    50  	shipper IndexQuerier
    51  }
    52  
    53  // NewIndexGateway instantiates a new Index Gateway and start its services.
    54  //
    55  // In case it is configured to be in ring mode, a Basic Service wrapping the ring client is started.
    56  // Otherwise, it starts an Idle Service that doesn't have lifecycle hooks.
    57  func NewIndexGateway(cfg Config, log log.Logger, registerer prometheus.Registerer, indexQuerier IndexQuerier, indexClient IndexClient) (*Gateway, error) {
    58  	g := &Gateway{
    59  		indexQuerier: indexQuerier,
    60  		cfg:          cfg,
    61  		log:          log,
    62  		indexClient:  indexClient,
    63  	}
    64  
    65  	g.Service = services.NewIdleService(nil, func(failureCase error) error {
    66  		g.indexQuerier.Stop()
    67  		g.indexClient.Stop()
    68  		return nil
    69  	})
    70  
    71  	return g, nil
    72  }
    73  
    74  func (g *Gateway) QueryIndex(request *logproto.QueryIndexRequest, server logproto.IndexGateway_QueryIndexServer) error {
    75  	var outerErr error
    76  	var innerErr error
    77  
    78  	queries := make([]index.Query, 0, len(request.Queries))
    79  	for _, query := range request.Queries {
    80  		queries = append(queries, index.Query{
    81  			TableName:        query.TableName,
    82  			HashValue:        query.HashValue,
    83  			RangeValuePrefix: query.RangeValuePrefix,
    84  			RangeValueStart:  query.RangeValueStart,
    85  			ValueEqual:       query.ValueEqual,
    86  		})
    87  	}
    88  
    89  	sendBatchMtx := sync.Mutex{}
    90  	outerErr = g.indexClient.QueryPages(server.Context(), queries, func(query index.Query, batch index.ReadBatchResult) bool {
    91  		innerErr = buildResponses(query, batch, func(response *logproto.QueryIndexResponse) error {
    92  			// do not send grpc responses concurrently. See https://github.com/grpc/grpc-go/blob/master/stream.go#L120-L123.
    93  			sendBatchMtx.Lock()
    94  			defer sendBatchMtx.Unlock()
    95  
    96  			return server.Send(response)
    97  		})
    98  
    99  		if innerErr != nil {
   100  			return false
   101  		}
   102  
   103  		return true
   104  	})
   105  
   106  	if innerErr != nil {
   107  		return innerErr
   108  	}
   109  
   110  	return outerErr
   111  }
   112  
   113  func buildResponses(query index.Query, batch index.ReadBatchResult, callback func(*logproto.QueryIndexResponse) error) error {
   114  	itr := batch.Iterator()
   115  	var resp []*logproto.Row
   116  
   117  	for itr.Next() {
   118  		if len(resp) == maxIndexEntriesPerResponse {
   119  			err := callback(&logproto.QueryIndexResponse{
   120  				QueryKey: util.QueryKey(query),
   121  				Rows:     resp,
   122  			})
   123  			if err != nil {
   124  				return err
   125  			}
   126  			resp = []*logproto.Row{}
   127  		}
   128  
   129  		resp = append(resp, &logproto.Row{
   130  			RangeValue: itr.RangeValue(),
   131  			Value:      itr.Value(),
   132  		})
   133  	}
   134  
   135  	if len(resp) != 0 {
   136  		err := callback(&logproto.QueryIndexResponse{
   137  			QueryKey: util.QueryKey(query),
   138  			Rows:     resp,
   139  		})
   140  		if err != nil {
   141  			return err
   142  		}
   143  	}
   144  
   145  	return nil
   146  }
   147  
   148  func (g *Gateway) GetChunkRef(ctx context.Context, req *logproto.GetChunkRefRequest) (*logproto.GetChunkRefResponse, error) {
   149  	instanceID, err := tenant.TenantID(ctx)
   150  	if err != nil {
   151  		return nil, err
   152  	}
   153  	matchers, err := syntax.ParseMatchers(req.Matchers)
   154  	if err != nil {
   155  		return nil, err
   156  	}
   157  	chunks, _, err := g.indexQuerier.GetChunkRefs(ctx, instanceID, req.From, req.Through, matchers...)
   158  	if err != nil {
   159  		return nil, err
   160  	}
   161  	result := &logproto.GetChunkRefResponse{
   162  		Refs: make([]*logproto.ChunkRef, 0, len(chunks)),
   163  	}
   164  	for _, cs := range chunks {
   165  		for i := range cs {
   166  			result.Refs = append(result.Refs, &cs[i].ChunkRef)
   167  		}
   168  	}
   169  	return result, nil
   170  }
   171  
   172  func (g *Gateway) GetSeries(ctx context.Context, req *logproto.GetSeriesRequest) (*logproto.GetSeriesResponse, error) {
   173  	instanceID, err := tenant.TenantID(ctx)
   174  	if err != nil {
   175  		return nil, err
   176  	}
   177  
   178  	matchers, err := syntax.ParseMatchers(req.Matchers)
   179  	if err != nil {
   180  		return nil, err
   181  	}
   182  	series, err := g.indexQuerier.GetSeries(ctx, instanceID, req.From, req.Through, matchers...)
   183  	if err != nil {
   184  		return nil, err
   185  	}
   186  
   187  	resp := &logproto.GetSeriesResponse{
   188  		Series: make([]logproto.IndexSeries, len(series)),
   189  	}
   190  	for i := range series {
   191  		resp.Series[i] = logproto.IndexSeries{
   192  			Labels: logproto.FromLabelsToLabelAdapters(series[i]),
   193  		}
   194  	}
   195  	return resp, nil
   196  }
   197  
   198  func (g *Gateway) LabelNamesForMetricName(ctx context.Context, req *logproto.LabelNamesForMetricNameRequest) (*logproto.LabelResponse, error) {
   199  	instanceID, err := tenant.TenantID(ctx)
   200  	if err != nil {
   201  		return nil, err
   202  	}
   203  	names, err := g.indexQuerier.LabelNamesForMetricName(ctx, instanceID, req.From, req.Through, req.MetricName)
   204  	if err != nil {
   205  		return nil, err
   206  	}
   207  	return &logproto.LabelResponse{
   208  		Values: names,
   209  	}, nil
   210  }
   211  
   212  func (g *Gateway) LabelValuesForMetricName(ctx context.Context, req *logproto.LabelValuesForMetricNameRequest) (*logproto.LabelResponse, error) {
   213  	instanceID, err := tenant.TenantID(ctx)
   214  	if err != nil {
   215  		return nil, err
   216  	}
   217  	var matchers []*labels.Matcher
   218  	// An empty matchers string cannot be parsed,
   219  	// therefore we check the string representation of the the matchers.
   220  	if req.Matchers != syntax.EmptyMatchers {
   221  		matchers, err = syntax.ParseMatchers(req.Matchers)
   222  		if err != nil {
   223  			return nil, err
   224  		}
   225  	}
   226  	names, err := g.indexQuerier.LabelValuesForMetricName(ctx, instanceID, req.From, req.Through, req.MetricName, req.LabelName, matchers...)
   227  	if err != nil {
   228  		return nil, err
   229  	}
   230  	return &logproto.LabelResponse{
   231  		Values: names,
   232  	}, nil
   233  }
   234  
   235  func (g *Gateway) GetStats(ctx context.Context, req *logproto.IndexStatsRequest) (*logproto.IndexStatsResponse, error) {
   236  	instanceID, err := tenant.TenantID(ctx)
   237  	if err != nil {
   238  		return nil, err
   239  	}
   240  	matchers, err := syntax.ParseMatchers(req.Matchers)
   241  	if err != nil {
   242  		return nil, err
   243  	}
   244  
   245  	return g.indexQuerier.Stats(ctx, instanceID, req.From, req.Through, matchers...)
   246  }