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 }