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 }