github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/querier/multi_tenant_querier.go (about) 1 package querier 2 3 import ( 4 "context" 5 6 "github.com/go-kit/log" 7 "github.com/prometheus/prometheus/model/labels" 8 "github.com/weaveworks/common/user" 9 10 "github.com/grafana/dskit/tenant" 11 12 "github.com/grafana/loki/pkg/iter" 13 "github.com/grafana/loki/pkg/loghttp" 14 "github.com/grafana/loki/pkg/logproto" 15 "github.com/grafana/loki/pkg/logql" 16 "github.com/grafana/loki/pkg/logql/syntax" 17 "github.com/grafana/loki/pkg/storage/stores/index/stats" 18 ) 19 20 const ( 21 defaultTenantLabel = "__tenant_id__" 22 retainExistingPrefix = "original_" 23 ) 24 25 // MultiTenantQuerier is able to query across different tenants. 26 type MultiTenantQuerier struct { 27 Querier 28 } 29 30 // NewMultiTenantQuerier returns a new querier able to query across different tenants. 31 func NewMultiTenantQuerier(querier Querier, logger log.Logger) *MultiTenantQuerier { 32 return &MultiTenantQuerier{ 33 Querier: querier, 34 } 35 } 36 37 func (q *MultiTenantQuerier) SelectLogs(ctx context.Context, params logql.SelectLogParams) (iter.EntryIterator, error) { 38 tenantIDs, err := tenant.TenantIDs(ctx) 39 if err != nil { 40 return nil, err 41 } 42 43 if len(tenantIDs) == 1 { 44 return q.Querier.SelectLogs(ctx, params) 45 } 46 47 selector, err := params.LogSelector() 48 if err != nil { 49 return nil, err 50 } 51 matchedTenants, filteredMatchers := filterValuesByMatchers(defaultTenantLabel, tenantIDs, selector.Matchers()...) 52 params.Selector = replaceMatchers(selector, filteredMatchers).String() 53 54 iters := make([]iter.EntryIterator, len(matchedTenants)) 55 i := 0 56 for id := range matchedTenants { 57 singleContext := user.InjectOrgID(ctx, id) 58 iter, err := q.Querier.SelectLogs(singleContext, params) 59 if err != nil { 60 return nil, err 61 } 62 63 iters[i] = NewTenantEntryIterator(iter, id) 64 i++ 65 } 66 return iter.NewSortEntryIterator(iters, params.Direction), nil 67 } 68 69 func (q *MultiTenantQuerier) SelectSamples(ctx context.Context, params logql.SelectSampleParams) (iter.SampleIterator, error) { 70 tenantIDs, err := tenant.TenantIDs(ctx) 71 if err != nil { 72 return nil, err 73 } 74 75 if len(tenantIDs) == 1 { 76 return q.Querier.SelectSamples(ctx, params) 77 } 78 79 matchedTenants, updatedSelector, err := removeTenantSelector(params, tenantIDs) 80 if err != nil { 81 return nil, err 82 } 83 params.Selector = updatedSelector.String() 84 85 iters := make([]iter.SampleIterator, len(matchedTenants)) 86 i := 0 87 for id := range matchedTenants { 88 singleContext := user.InjectOrgID(ctx, id) 89 iter, err := q.Querier.SelectSamples(singleContext, params) 90 if err != nil { 91 return nil, err 92 } 93 94 iters[i] = NewTenantSampleIterator(iter, id) 95 i++ 96 } 97 return iter.NewSortSampleIterator(iters), nil 98 } 99 100 func (q *MultiTenantQuerier) Label(ctx context.Context, req *logproto.LabelRequest) (*logproto.LabelResponse, error) { 101 tenantIDs, err := tenant.TenantIDs(ctx) 102 if err != nil { 103 return nil, err 104 } 105 106 if req.Values && req.Name == defaultTenantLabel { 107 return &logproto.LabelResponse{Values: tenantIDs}, nil 108 } 109 110 if len(tenantIDs) == 1 { 111 return q.Querier.Label(ctx, req) 112 } 113 114 responses := make([]*logproto.LabelResponse, len(tenantIDs)) 115 for i, id := range tenantIDs { 116 singleContext := user.InjectOrgID(ctx, id) 117 resp, err := q.Querier.Label(singleContext, req) 118 if err != nil { 119 return nil, err 120 } 121 122 responses[i] = resp 123 } 124 125 // Append tenant ID label name if label names are requested. 126 if !req.Values { 127 responses = append(responses, &logproto.LabelResponse{Values: []string{defaultTenantLabel}}) 128 } 129 130 return logproto.MergeLabelResponses(responses) 131 } 132 133 func (q *MultiTenantQuerier) Series(ctx context.Context, req *logproto.SeriesRequest) (*logproto.SeriesResponse, error) { 134 tenantIDs, err := tenant.TenantIDs(ctx) 135 if err != nil { 136 return nil, err 137 } 138 139 if len(tenantIDs) == 1 { 140 return q.Querier.Series(ctx, req) 141 } 142 143 responses := make([]*logproto.SeriesResponse, len(tenantIDs)) 144 for i, id := range tenantIDs { 145 singleContext := user.InjectOrgID(ctx, id) 146 resp, err := q.Querier.Series(singleContext, req) 147 if err != nil { 148 return nil, err 149 } 150 151 for _, s := range resp.GetSeries() { 152 if _, ok := s.Labels[defaultTenantLabel]; !ok { 153 s.Labels[defaultTenantLabel] = id 154 } 155 } 156 157 responses[i] = resp 158 } 159 160 return logproto.MergeSeriesResponses(responses) 161 } 162 163 func (q *MultiTenantQuerier) IndexStats(ctx context.Context, req *loghttp.RangeQuery) (*stats.Stats, error) { 164 tenantIDs, err := tenant.TenantIDs(ctx) 165 if err != nil { 166 return nil, err 167 } 168 169 if len(tenantIDs) == 1 { 170 return q.Querier.IndexStats(ctx, req) 171 } 172 173 responses := make([]*stats.Stats, len(tenantIDs)) 174 for i, id := range tenantIDs { 175 singleContext := user.InjectOrgID(ctx, id) 176 resp, err := q.Querier.IndexStats(singleContext, req) 177 if err != nil { 178 return nil, err 179 } 180 181 responses[i] = resp 182 } 183 184 merged := stats.MergeStats(responses...) 185 186 return &merged, nil 187 } 188 189 // removeTenantSelector filters the given tenant IDs based on any tenant ID filter the in passed selector. 190 func removeTenantSelector(params logql.SelectSampleParams, tenantIDs []string) (map[string]struct{}, syntax.Expr, error) { 191 expr, err := params.Expr() 192 if err != nil { 193 return nil, nil, err 194 } 195 matchedTenants, filteredMatchers := filterValuesByMatchers(defaultTenantLabel, tenantIDs, expr.Selector().Matchers()...) 196 updatedExpr := replaceMatchers(expr, filteredMatchers) 197 return matchedTenants, updatedExpr, nil 198 } 199 200 // replaceMatchers traverses the passed expression and replaces all matchers. 201 func replaceMatchers(expr syntax.Expr, matchers []*labels.Matcher) syntax.Expr { 202 expr, _ = syntax.Clone(expr) 203 expr.Walk(func(e interface{}) { 204 switch concrete := e.(type) { 205 case *syntax.MatchersExpr: 206 concrete.Mts = matchers 207 } 208 }) 209 return expr 210 } 211 212 // See https://github.com/grafana/mimir/blob/114ab88b50638a2047e2ca2a60640f6ca6fe8c17/pkg/querier/tenantfederation/tenant_federation.go#L29-L69 213 // filterValuesByMatchers applies matchers to inputed `idLabelName` and 214 // `ids`. A set of matched IDs is returned and also all label matchers not 215 // targeting the `idLabelName` label. 216 // 217 // In case a label matcher is set on a label conflicting with `idLabelName`, we 218 // need to rename this labelMatcher's name to its original name. This is used 219 // to as part of Select in the mergeQueryable, to ensure only relevant queries 220 // are considered and the forwarded matchers do not contain matchers on the 221 // `idLabelName`. 222 func filterValuesByMatchers(idLabelName string, ids []string, matchers ...*labels.Matcher) (matchedIDs map[string]struct{}, unrelatedMatchers []*labels.Matcher) { 223 // this contains the matchers which are not related to idLabelName 224 unrelatedMatchers = make([]*labels.Matcher, 0, len(matchers)) 225 226 // build map of values to consider for the matchers 227 matchedIDs = sliceToSet(ids) 228 229 for _, m := range matchers { 230 switch m.Name { 231 // matcher has idLabelName to target a specific tenant(s) 232 case idLabelName: 233 for value := range matchedIDs { 234 if !m.Matches(value) { 235 delete(matchedIDs, value) 236 } 237 } 238 239 // check if has the retained label name 240 case retainExistingPrefix + idLabelName: 241 // rewrite label to the original name, by copying matcher and 242 // replacing the label name 243 rewrittenM := *m 244 rewrittenM.Name = idLabelName 245 unrelatedMatchers = append(unrelatedMatchers, &rewrittenM) 246 247 default: 248 unrelatedMatchers = append(unrelatedMatchers, m) 249 } 250 } 251 252 return matchedIDs, unrelatedMatchers 253 } 254 255 func sliceToSet(values []string) map[string]struct{} { 256 out := make(map[string]struct{}, len(values)) 257 for _, v := range values { 258 out[v] = struct{}{} 259 } 260 return out 261 } 262 263 type relabel struct { 264 tenantID string 265 cache map[string]labels.Labels 266 } 267 268 func (r relabel) relabel(original string) string { 269 lbls, ok := r.cache[original] 270 if ok { 271 return lbls.String() 272 } 273 274 lbls, _ = syntax.ParseLabels(original) 275 builder := labels.NewBuilder(lbls).Del(defaultTenantLabel) 276 277 // Prefix label if it conflicts with the tenant label. 278 if lbls.Has(defaultTenantLabel) { 279 builder.Set(retainExistingPrefix+defaultTenantLabel, lbls.Get(defaultTenantLabel)) 280 } 281 builder.Set(defaultTenantLabel, r.tenantID) 282 283 lbls = builder.Labels() 284 r.cache[original] = lbls 285 return lbls.String() 286 } 287 288 // TenantEntry Iterator wraps an entry iterator and adds the tenant label. 289 type TenantEntryIterator struct { 290 iter.EntryIterator 291 relabel 292 } 293 294 func NewTenantEntryIterator(iter iter.EntryIterator, id string) *TenantEntryIterator { 295 return &TenantEntryIterator{ 296 EntryIterator: iter, 297 relabel: relabel{ 298 tenantID: id, 299 cache: map[string]labels.Labels{}, 300 }, 301 } 302 } 303 304 func (i *TenantEntryIterator) Labels() string { 305 return i.relabel.relabel(i.EntryIterator.Labels()) 306 } 307 308 // TenantEntry Iterator wraps a sample iterator and adds the tenant label. 309 type TenantSampleIterator struct { 310 iter.SampleIterator 311 relabel 312 } 313 314 func NewTenantSampleIterator(iter iter.SampleIterator, id string) *TenantSampleIterator { 315 return &TenantSampleIterator{ 316 SampleIterator: iter, 317 relabel: relabel{ 318 tenantID: id, 319 cache: map[string]labels.Labels{}, 320 }, 321 } 322 323 } 324 325 func (i *TenantSampleIterator) Labels() string { 326 return i.relabel.relabel(i.SampleIterator.Labels()) 327 }