github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/querier/ingester_querier.go (about)

     1  package querier
     2  
     3  import (
     4  	"context"
     5  	"net/http"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/gogo/status"
    10  	"github.com/grafana/dskit/ring"
    11  	ring_client "github.com/grafana/dskit/ring/client"
    12  	"github.com/grafana/dskit/services"
    13  	"github.com/pkg/errors"
    14  	"github.com/prometheus/common/model"
    15  	"github.com/prometheus/prometheus/model/labels"
    16  	"github.com/weaveworks/common/httpgrpc"
    17  	"google.golang.org/grpc/codes"
    18  
    19  	"github.com/grafana/loki/pkg/distributor/clientpool"
    20  	"github.com/grafana/loki/pkg/ingester/client"
    21  	"github.com/grafana/loki/pkg/iter"
    22  	"github.com/grafana/loki/pkg/logproto"
    23  	"github.com/grafana/loki/pkg/logql"
    24  	"github.com/grafana/loki/pkg/logql/syntax"
    25  	"github.com/grafana/loki/pkg/logqlmodel/stats"
    26  	index_stats "github.com/grafana/loki/pkg/storage/stores/index/stats"
    27  	util_log "github.com/grafana/loki/pkg/util/log"
    28  )
    29  
    30  type responseFromIngesters struct {
    31  	addr     string
    32  	response interface{}
    33  }
    34  
    35  // IngesterQuerier helps with querying the ingesters.
    36  type IngesterQuerier struct {
    37  	ring            ring.ReadRing
    38  	pool            *ring_client.Pool
    39  	extraQueryDelay time.Duration
    40  }
    41  
    42  func NewIngesterQuerier(clientCfg client.Config, ring ring.ReadRing, extraQueryDelay time.Duration) (*IngesterQuerier, error) {
    43  	factory := func(addr string) (ring_client.PoolClient, error) {
    44  		return client.New(clientCfg, addr)
    45  	}
    46  
    47  	return newIngesterQuerier(clientCfg, ring, extraQueryDelay, factory)
    48  }
    49  
    50  // newIngesterQuerier creates a new IngesterQuerier and allows to pass a custom ingester client factory
    51  // used for testing purposes
    52  func newIngesterQuerier(clientCfg client.Config, ring ring.ReadRing, extraQueryDelay time.Duration, clientFactory ring_client.PoolFactory) (*IngesterQuerier, error) {
    53  	iq := IngesterQuerier{
    54  		ring:            ring,
    55  		pool:            clientpool.NewPool(clientCfg.PoolConfig, ring, clientFactory, util_log.Logger),
    56  		extraQueryDelay: extraQueryDelay,
    57  	}
    58  
    59  	err := services.StartAndAwaitRunning(context.Background(), iq.pool)
    60  	if err != nil {
    61  		return nil, errors.Wrap(err, "querier pool")
    62  	}
    63  
    64  	return &iq, nil
    65  }
    66  
    67  // forAllIngesters runs f, in parallel, for all ingesters
    68  // TODO taken from Cortex, see if we can refactor out an usable interface.
    69  func (q *IngesterQuerier) forAllIngesters(ctx context.Context, f func(logproto.QuerierClient) (interface{}, error)) ([]responseFromIngesters, error) {
    70  	replicationSet, err := q.ring.GetReplicationSetForOperation(ring.Read)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  
    75  	return q.forGivenIngesters(ctx, replicationSet, f)
    76  }
    77  
    78  // forGivenIngesters runs f, in parallel, for given ingesters
    79  // TODO taken from Cortex, see if we can refactor out an usable interface.
    80  func (q *IngesterQuerier) forGivenIngesters(ctx context.Context, replicationSet ring.ReplicationSet, f func(logproto.QuerierClient) (interface{}, error)) ([]responseFromIngesters, error) {
    81  	results, err := replicationSet.Do(ctx, q.extraQueryDelay, func(ctx context.Context, ingester *ring.InstanceDesc) (interface{}, error) {
    82  		client, err := q.pool.GetClientFor(ingester.Addr)
    83  		if err != nil {
    84  			return nil, err
    85  		}
    86  
    87  		resp, err := f(client.(logproto.QuerierClient))
    88  		if err != nil {
    89  			return nil, err
    90  		}
    91  
    92  		return responseFromIngesters{ingester.Addr, resp}, nil
    93  	})
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  
    98  	responses := make([]responseFromIngesters, 0, len(results))
    99  	for _, result := range results {
   100  		responses = append(responses, result.(responseFromIngesters))
   101  	}
   102  
   103  	return responses, err
   104  }
   105  
   106  func (q *IngesterQuerier) SelectLogs(ctx context.Context, params logql.SelectLogParams) ([]iter.EntryIterator, error) {
   107  	resps, err := q.forAllIngesters(ctx, func(client logproto.QuerierClient) (interface{}, error) {
   108  		stats.FromContext(ctx).AddIngesterReached(1)
   109  		return client.Query(ctx, params.QueryRequest)
   110  	})
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  
   115  	iterators := make([]iter.EntryIterator, len(resps))
   116  	for i := range resps {
   117  		iterators[i] = iter.NewQueryClientIterator(resps[i].response.(logproto.Querier_QueryClient), params.Direction)
   118  	}
   119  	return iterators, nil
   120  }
   121  
   122  func (q *IngesterQuerier) SelectSample(ctx context.Context, params logql.SelectSampleParams) ([]iter.SampleIterator, error) {
   123  	resps, err := q.forAllIngesters(ctx, func(client logproto.QuerierClient) (interface{}, error) {
   124  		stats.FromContext(ctx).AddIngesterReached(1)
   125  		return client.QuerySample(ctx, params.SampleQueryRequest)
   126  	})
   127  	if err != nil {
   128  		return nil, err
   129  	}
   130  
   131  	iterators := make([]iter.SampleIterator, len(resps))
   132  	for i := range resps {
   133  		iterators[i] = iter.NewSampleQueryClientIterator(resps[i].response.(logproto.Querier_QuerySampleClient))
   134  	}
   135  	return iterators, nil
   136  }
   137  
   138  func (q *IngesterQuerier) Label(ctx context.Context, req *logproto.LabelRequest) ([][]string, error) {
   139  	resps, err := q.forAllIngesters(ctx, func(client logproto.QuerierClient) (interface{}, error) {
   140  		return client.Label(ctx, req)
   141  	})
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  
   146  	results := make([][]string, 0, len(resps))
   147  	for _, resp := range resps {
   148  		results = append(results, resp.response.(*logproto.LabelResponse).Values)
   149  	}
   150  
   151  	return results, nil
   152  }
   153  
   154  func (q *IngesterQuerier) Tail(ctx context.Context, req *logproto.TailRequest) (map[string]logproto.Querier_TailClient, error) {
   155  	resps, err := q.forAllIngesters(ctx, func(client logproto.QuerierClient) (interface{}, error) {
   156  		return client.Tail(ctx, req)
   157  	})
   158  	if err != nil {
   159  		return nil, err
   160  	}
   161  
   162  	tailClients := make(map[string]logproto.Querier_TailClient)
   163  	for i := range resps {
   164  		tailClients[resps[i].addr] = resps[i].response.(logproto.Querier_TailClient)
   165  	}
   166  
   167  	return tailClients, nil
   168  }
   169  
   170  func (q *IngesterQuerier) TailDisconnectedIngesters(ctx context.Context, req *logproto.TailRequest, connectedIngestersAddr []string) (map[string]logproto.Querier_TailClient, error) {
   171  	// Build a map to easily check if an ingester address is already connected
   172  	connected := make(map[string]bool)
   173  	for _, addr := range connectedIngestersAddr {
   174  		connected[addr] = true
   175  	}
   176  
   177  	// Get the current replication set from the ring
   178  	replicationSet, err := q.ring.GetReplicationSetForOperation(ring.Read)
   179  	if err != nil {
   180  		return nil, err
   181  	}
   182  
   183  	// Look for disconnected ingesters or new one we should (re)connect to
   184  	reconnectIngesters := []ring.InstanceDesc{}
   185  
   186  	for _, ingester := range replicationSet.Instances {
   187  		if _, ok := connected[ingester.Addr]; ok {
   188  			continue
   189  		}
   190  
   191  		// Skip ingesters which are leaving or joining the cluster
   192  		if ingester.State != ring.ACTIVE {
   193  			continue
   194  		}
   195  
   196  		reconnectIngesters = append(reconnectIngesters, ingester)
   197  	}
   198  
   199  	if len(reconnectIngesters) == 0 {
   200  		return nil, nil
   201  	}
   202  
   203  	// Instance a tail client for each ingester to re(connect)
   204  	reconnectClients, err := q.forGivenIngesters(ctx, ring.ReplicationSet{Instances: reconnectIngesters}, func(client logproto.QuerierClient) (interface{}, error) {
   205  		return client.Tail(ctx, req)
   206  	})
   207  	if err != nil {
   208  		return nil, err
   209  	}
   210  
   211  	reconnectClientsMap := make(map[string]logproto.Querier_TailClient)
   212  	for _, client := range reconnectClients {
   213  		reconnectClientsMap[client.addr] = client.response.(logproto.Querier_TailClient)
   214  	}
   215  
   216  	return reconnectClientsMap, nil
   217  }
   218  
   219  func (q *IngesterQuerier) Series(ctx context.Context, req *logproto.SeriesRequest) ([][]logproto.SeriesIdentifier, error) {
   220  	resps, err := q.forAllIngesters(ctx, func(client logproto.QuerierClient) (interface{}, error) {
   221  		return client.Series(ctx, req)
   222  	})
   223  	if err != nil {
   224  		return nil, err
   225  	}
   226  	var acc [][]logproto.SeriesIdentifier
   227  	for _, resp := range resps {
   228  		acc = append(acc, resp.response.(*logproto.SeriesResponse).Series)
   229  	}
   230  
   231  	return acc, nil
   232  }
   233  
   234  func (q *IngesterQuerier) TailersCount(ctx context.Context) ([]uint32, error) {
   235  	replicationSet, err := q.ring.GetAllHealthy(ring.Read)
   236  	if err != nil {
   237  		return nil, err
   238  	}
   239  
   240  	// we want to check count of active tailers with only active ingesters
   241  	ingesters := make([]ring.InstanceDesc, 0, 1)
   242  	for i := range replicationSet.Instances {
   243  		if replicationSet.Instances[i].State == ring.ACTIVE {
   244  			ingesters = append(ingesters, replicationSet.Instances[i])
   245  		}
   246  	}
   247  
   248  	if len(ingesters) == 0 {
   249  		return nil, httpgrpc.Errorf(http.StatusInternalServerError, "no active ingester found")
   250  	}
   251  
   252  	responses, err := q.forGivenIngesters(ctx, replicationSet, func(querierClient logproto.QuerierClient) (interface{}, error) {
   253  		resp, err := querierClient.TailersCount(ctx, &logproto.TailersCountRequest{})
   254  		if err != nil {
   255  			return nil, err
   256  		}
   257  		return resp.Count, nil
   258  	})
   259  	// We are only checking active ingesters, and any error returned stops checking other ingesters
   260  	// so return that error here as well.
   261  	if err != nil {
   262  		return nil, err
   263  	}
   264  
   265  	counts := make([]uint32, len(responses))
   266  
   267  	for _, resp := range responses {
   268  		counts = append(counts, resp.response.(uint32))
   269  	}
   270  
   271  	return counts, nil
   272  }
   273  
   274  func (q *IngesterQuerier) GetChunkIDs(ctx context.Context, from, through model.Time, matchers ...*labels.Matcher) ([]string, error) {
   275  	resps, err := q.forAllIngesters(ctx, func(querierClient logproto.QuerierClient) (interface{}, error) {
   276  		return querierClient.GetChunkIDs(ctx, &logproto.GetChunkIDsRequest{
   277  			Matchers: convertMatchersToString(matchers),
   278  			Start:    from.Time(),
   279  			End:      through.Time(),
   280  		})
   281  	})
   282  	if err != nil {
   283  		return nil, err
   284  	}
   285  
   286  	var chunkIDs []string
   287  	for i := range resps {
   288  		chunkIDs = append(chunkIDs, resps[i].response.(*logproto.GetChunkIDsResponse).ChunkIDs...)
   289  	}
   290  
   291  	return chunkIDs, nil
   292  }
   293  
   294  func (q *IngesterQuerier) Stats(ctx context.Context, userID string, from, through model.Time, matchers ...*labels.Matcher) (*index_stats.Stats, error) {
   295  	resps, err := q.forAllIngesters(ctx, func(querierClient logproto.QuerierClient) (interface{}, error) {
   296  		return querierClient.GetStats(ctx, &logproto.IndexStatsRequest{
   297  			From:     from,
   298  			Through:  through,
   299  			Matchers: syntax.MatchersString(matchers),
   300  		})
   301  	})
   302  
   303  	if err != nil {
   304  		if isUnimplementedCallError(err) {
   305  			// Handle communication with older ingesters gracefully
   306  			return &index_stats.Stats{}, nil
   307  		}
   308  		return nil, err
   309  	}
   310  
   311  	casted := make([]*index_stats.Stats, 0, len(resps))
   312  	for _, resp := range resps {
   313  		casted = append(casted, resp.response.(*index_stats.Stats))
   314  	}
   315  
   316  	merged := index_stats.MergeStats(casted...)
   317  	return &merged, nil
   318  }
   319  
   320  func convertMatchersToString(matchers []*labels.Matcher) string {
   321  	out := strings.Builder{}
   322  	out.WriteRune('{')
   323  
   324  	for idx, m := range matchers {
   325  		if idx > 0 {
   326  			out.WriteRune(',')
   327  		}
   328  
   329  		out.WriteString(m.String())
   330  	}
   331  
   332  	out.WriteRune('}')
   333  	return out.String()
   334  }
   335  
   336  // isUnimplementedCallError tells if the GRPC error is a gRPC error with code Unimplemented.
   337  func isUnimplementedCallError(err error) bool {
   338  	if err == nil {
   339  		return false
   340  	}
   341  
   342  	s, ok := status.FromError(err)
   343  	if !ok {
   344  		return false
   345  	}
   346  	return (s.Code() == codes.Unimplemented)
   347  }