github.com/muhammadn/cortex@v1.9.1-0.20220510110439-46bb7000d03d/pkg/querier/tenantfederation/merge_queryable.go (about) 1 package tenantfederation 2 3 import ( 4 "context" 5 "fmt" 6 "sort" 7 "strings" 8 9 "github.com/grafana/dskit/concurrency" 10 "github.com/pkg/errors" 11 "github.com/prometheus/prometheus/pkg/labels" 12 "github.com/prometheus/prometheus/storage" 13 "github.com/prometheus/prometheus/tsdb/chunkenc" 14 tsdb_errors "github.com/prometheus/prometheus/tsdb/errors" 15 "github.com/weaveworks/common/user" 16 17 "github.com/cortexproject/cortex/pkg/tenant" 18 "github.com/cortexproject/cortex/pkg/util/spanlogger" 19 ) 20 21 const ( 22 defaultTenantLabel = "__tenant_id__" 23 retainExistingPrefix = "original_" 24 maxConcurrency = 16 25 ) 26 27 // NewQueryable returns a queryable that iterates through all the tenant IDs 28 // that are part of the request and aggregates the results from each tenant's 29 // Querier by sending of subsequent requests. 30 // By setting byPassWithSingleQuerier to true the mergeQuerier gets by-passed 31 // and results for request with a single querier will not contain the 32 // "__tenant_id__" label. This allows a smoother transition, when enabling 33 // tenant federation in a cluster. 34 // The result contains a label "__tenant_id__" to identify the tenant ID that 35 // it originally resulted from. 36 // If the label "__tenant_id__" is already existing, its value is overwritten 37 // by the tenant ID and the previous value is exposed through a new label 38 // prefixed with "original_". This behaviour is not implemented recursively. 39 func NewQueryable(upstream storage.Queryable, byPassWithSingleQuerier bool) storage.Queryable { 40 return NewMergeQueryable(defaultTenantLabel, tenantQuerierCallback(upstream), byPassWithSingleQuerier) 41 } 42 43 func tenantQuerierCallback(queryable storage.Queryable) MergeQuerierCallback { 44 return func(ctx context.Context, mint int64, maxt int64) ([]string, []storage.Querier, error) { 45 tenantIDs, err := tenant.TenantIDs(ctx) 46 if err != nil { 47 return nil, nil, err 48 } 49 50 var queriers = make([]storage.Querier, len(tenantIDs)) 51 for pos, tenantID := range tenantIDs { 52 q, err := queryable.Querier( 53 user.InjectOrgID(ctx, tenantID), 54 mint, 55 maxt, 56 ) 57 if err != nil { 58 return nil, nil, err 59 } 60 queriers[pos] = q 61 } 62 63 return tenantIDs, queriers, nil 64 } 65 } 66 67 // MergeQuerierCallback returns the underlying queriers and their IDs relevant 68 // for the query. 69 type MergeQuerierCallback func(ctx context.Context, mint int64, maxt int64) (ids []string, queriers []storage.Querier, err error) 70 71 // NewMergeQueryable returns a queryable that merges results from multiple 72 // underlying Queryables. The underlying queryables and its label values to be 73 // considered are returned by a MergeQuerierCallback. 74 // By setting byPassWithSingleQuerier to true the mergeQuerier gets by-passed 75 // and results for request with a single querier will not contain the id label. 76 // This allows a smoother transition, when enabling tenant federation in a 77 // cluster. 78 // Results contain a label `idLabelName` to identify the underlying queryable 79 // that it originally resulted from. 80 // If the label `idLabelName` is already existing, its value is overwritten and 81 // the previous value is exposed through a new label prefixed with "original_". 82 // This behaviour is not implemented recursively. 83 func NewMergeQueryable(idLabelName string, callback MergeQuerierCallback, byPassWithSingleQuerier bool) storage.Queryable { 84 return &mergeQueryable{ 85 idLabelName: idLabelName, 86 callback: callback, 87 byPassWithSingleQuerier: byPassWithSingleQuerier, 88 } 89 } 90 91 type mergeQueryable struct { 92 idLabelName string 93 byPassWithSingleQuerier bool 94 callback MergeQuerierCallback 95 } 96 97 // Querier returns a new mergeQuerier, which aggregates results from multiple 98 // underlying queriers into a single result. 99 func (m *mergeQueryable) Querier(ctx context.Context, mint int64, maxt int64) (storage.Querier, error) { 100 // TODO: it's necessary to think how to override context inside querier 101 // to mark spans created inside querier as child of a span created inside 102 // methods of merged querier. 103 ids, queriers, err := m.callback(ctx, mint, maxt) 104 if err != nil { 105 return nil, err 106 } 107 108 // by pass when only single querier is returned 109 if m.byPassWithSingleQuerier && len(queriers) == 1 { 110 return queriers[0], nil 111 } 112 113 return &mergeQuerier{ 114 ctx: ctx, 115 idLabelName: m.idLabelName, 116 queriers: queriers, 117 ids: ids, 118 }, nil 119 } 120 121 // mergeQuerier aggregates the results from underlying queriers and adds a 122 // label `idLabelName` to identify the queryable that the metric resulted 123 // from. 124 // If the label `idLabelName` is already existing, its value is overwritten and 125 // the previous value is exposed through a new label prefixed with "original_". 126 // This behaviour is not implemented recursively 127 type mergeQuerier struct { 128 ctx context.Context 129 queriers []storage.Querier 130 idLabelName string 131 ids []string 132 } 133 134 // LabelValues returns all potential values for a label name. It is not safe 135 // to use the strings beyond the lifefime of the querier. 136 // For the label `idLabelName` it will return all the underlying ids available. 137 // For the label "original_" + `idLabelName it will return all the values 138 // of the underlying queriers for `idLabelName`. 139 func (m *mergeQuerier) LabelValues(name string, matchers ...*labels.Matcher) ([]string, storage.Warnings, error) { 140 log, _ := spanlogger.New(m.ctx, "mergeQuerier.LabelValues") 141 defer log.Span.Finish() 142 143 matchedTenants, filteredMatchers := filterValuesByMatchers(m.idLabelName, m.ids, matchers...) 144 145 if name == m.idLabelName { 146 var labelValues = make([]string, 0, len(matchedTenants)) 147 for _, id := range m.ids { 148 if _, matched := matchedTenants[id]; matched { 149 labelValues = append(labelValues, id) 150 } 151 } 152 return labelValues, nil, nil 153 } 154 155 // ensure the name of a retained label gets handled under the original 156 // label name 157 if name == retainExistingPrefix+m.idLabelName { 158 name = m.idLabelName 159 } 160 161 return m.mergeDistinctStringSliceWithTenants(func(ctx context.Context, q storage.Querier) ([]string, storage.Warnings, error) { 162 return q.LabelValues(name, filteredMatchers...) 163 }, matchedTenants) 164 } 165 166 // LabelNames returns all the unique label names present in the underlying 167 // queriers. It also adds the `idLabelName` and if present in the original 168 // results the original `idLabelName`. 169 func (m *mergeQuerier) LabelNames(matchers ...*labels.Matcher) ([]string, storage.Warnings, error) { 170 log, _ := spanlogger.New(m.ctx, "mergeQuerier.LabelNames") 171 defer log.Span.Finish() 172 173 matchedTenants, filteredMatchers := filterValuesByMatchers(m.idLabelName, m.ids, matchers...) 174 175 labelNames, warnings, err := m.mergeDistinctStringSliceWithTenants(func(ctx context.Context, q storage.Querier) ([]string, storage.Warnings, error) { 176 return q.LabelNames(filteredMatchers...) 177 }, matchedTenants) 178 if err != nil { 179 return nil, nil, err 180 } 181 182 // check if the `idLabelName` exists in the original result 183 var idLabelNameExists bool 184 labelPos := sort.SearchStrings(labelNames, m.idLabelName) 185 if labelPos < len(labelNames) && labelNames[labelPos] == m.idLabelName { 186 idLabelNameExists = true 187 } 188 189 labelToAdd := m.idLabelName 190 191 // if `idLabelName` already exists, we need to add the name prefix with 192 // retainExistingPrefix. 193 if idLabelNameExists { 194 labelToAdd = retainExistingPrefix + m.idLabelName 195 labelPos = sort.SearchStrings(labelNames, labelToAdd) 196 } 197 198 // insert label at the correct position 199 labelNames = append(labelNames, "") 200 copy(labelNames[labelPos+1:], labelNames[labelPos:]) 201 labelNames[labelPos] = labelToAdd 202 203 return labelNames, warnings, nil 204 } 205 206 type stringSliceFunc func(context.Context, storage.Querier) ([]string, storage.Warnings, error) 207 208 type stringSliceFuncJob struct { 209 querier storage.Querier 210 id string 211 result []string 212 warnings storage.Warnings 213 } 214 215 // mergeDistinctStringSliceWithTenants aggregates stringSliceFunc call 216 // results from queriers whose tenant ids match the tenants map. If a nil map is 217 // provided, all queriers are used. It removes duplicates and sorts the result. 218 // It doesn't require the output of the stringSliceFunc to be sorted, as results 219 // of LabelValues are not sorted. 220 func (m *mergeQuerier) mergeDistinctStringSliceWithTenants(f stringSliceFunc, tenants map[string]struct{}) ([]string, storage.Warnings, error) { 221 var jobs []interface{} 222 223 for pos, id := range m.ids { 224 if tenants != nil { 225 if _, matched := tenants[id]; !matched { 226 continue 227 } 228 } 229 230 jobs = append(jobs, &stringSliceFuncJob{ 231 querier: m.queriers[pos], 232 id: m.ids[pos], 233 }) 234 } 235 236 run := func(ctx context.Context, jobIntf interface{}) error { 237 job, ok := jobIntf.(*stringSliceFuncJob) 238 if !ok { 239 return fmt.Errorf("unexpected type %T", jobIntf) 240 } 241 242 var err error 243 job.result, job.warnings, err = f(ctx, job.querier) 244 if err != nil { 245 return errors.Wrapf(err, "error querying %s %s", rewriteLabelName(m.idLabelName), job.id) 246 } 247 248 return nil 249 } 250 251 err := concurrency.ForEach(m.ctx, jobs, maxConcurrency, run) 252 if err != nil { 253 return nil, nil, err 254 } 255 256 // aggregate warnings and deduplicate string results 257 var warnings storage.Warnings 258 resultMap := make(map[string]struct{}) 259 for _, jobIntf := range jobs { 260 job, ok := jobIntf.(*stringSliceFuncJob) 261 if !ok { 262 return nil, nil, fmt.Errorf("unexpected type %T", jobIntf) 263 } 264 265 for _, e := range job.result { 266 resultMap[e] = struct{}{} 267 } 268 269 for _, w := range job.warnings { 270 warnings = append(warnings, errors.Wrapf(w, "warning querying %s %s", rewriteLabelName(m.idLabelName), job.id)) 271 } 272 } 273 274 var result = make([]string, 0, len(resultMap)) 275 for e := range resultMap { 276 result = append(result, e) 277 } 278 sort.Strings(result) 279 return result, warnings, nil 280 } 281 282 // Close releases the resources of the Querier. 283 func (m *mergeQuerier) Close() error { 284 errs := tsdb_errors.NewMulti() 285 for pos, id := range m.ids { 286 errs.Add(errors.Wrapf(m.queriers[pos].Close(), "failed to close querier for %s %s", rewriteLabelName(m.idLabelName), id)) 287 } 288 return errs.Err() 289 } 290 291 type selectJob struct { 292 pos int 293 querier storage.Querier 294 id string 295 } 296 297 // Select returns a set of series that matches the given label matchers. If the 298 // `idLabelName` is matched on, it only considers those queriers 299 // matching. The forwarded labelSelector is not containing those that operate 300 // on `idLabelName`. 301 func (m *mergeQuerier) Select(sortSeries bool, hints *storage.SelectHints, matchers ...*labels.Matcher) storage.SeriesSet { 302 log, ctx := spanlogger.New(m.ctx, "mergeQuerier.Select") 303 defer log.Span.Finish() 304 matchedValues, filteredMatchers := filterValuesByMatchers(m.idLabelName, m.ids, matchers...) 305 var jobs = make([]interface{}, len(matchedValues)) 306 var seriesSets = make([]storage.SeriesSet, len(matchedValues)) 307 var jobPos int 308 for labelPos := range m.ids { 309 if _, matched := matchedValues[m.ids[labelPos]]; !matched { 310 continue 311 } 312 jobs[jobPos] = &selectJob{ 313 pos: jobPos, 314 querier: m.queriers[labelPos], 315 id: m.ids[labelPos], 316 } 317 jobPos++ 318 } 319 320 run := func(ctx context.Context, jobIntf interface{}) error { 321 job, ok := jobIntf.(*selectJob) 322 if !ok { 323 return fmt.Errorf("unexpected type %T", jobIntf) 324 } 325 seriesSets[job.pos] = &addLabelsSeriesSet{ 326 upstream: job.querier.Select(sortSeries, hints, filteredMatchers...), 327 labels: labels.Labels{ 328 { 329 Name: m.idLabelName, 330 Value: job.id, 331 }, 332 }, 333 } 334 return nil 335 } 336 337 err := concurrency.ForEach(ctx, jobs, maxConcurrency, run) 338 if err != nil { 339 return storage.ErrSeriesSet(err) 340 } 341 342 return storage.NewMergeSeriesSet(seriesSets, storage.ChainedSeriesMerge) 343 } 344 345 // filterValuesByMatchers applies matchers to inputed `idLabelName` and 346 // `ids`. A map of matched values is returned and also all label matchers not 347 // matching the `idLabelName`. 348 // In case a label matcher is set on a label conflicting with `idLabelName`, we 349 // need to rename this labelMatcher's name to its original name. This is used 350 // to as part of Select in the mergeQueryable, to ensure only relevant queries 351 // are considered and the forwarded matchers do not contain matchers on the 352 // `idLabelName`. 353 func filterValuesByMatchers(idLabelName string, ids []string, matchers ...*labels.Matcher) (matchedIDs map[string]struct{}, unrelatedMatchers []*labels.Matcher) { 354 // this contains the matchers which are not related to idLabelName 355 unrelatedMatchers = make([]*labels.Matcher, 0, len(matchers)) 356 357 // build map of values to consider for the matchers 358 matchedIDs = make(map[string]struct{}, len(ids)) 359 for _, value := range ids { 360 matchedIDs[value] = struct{}{} 361 } 362 363 for _, m := range matchers { 364 switch m.Name { 365 // matcher has idLabelName to target a specific tenant(s) 366 case idLabelName: 367 for value := range matchedIDs { 368 if !m.Matches(value) { 369 delete(matchedIDs, value) 370 } 371 } 372 373 // check if has the retained label name 374 case retainExistingPrefix + idLabelName: 375 // rewrite label to the original name, by copying matcher and 376 // replacing the label name 377 rewrittenM := *m 378 rewrittenM.Name = idLabelName 379 unrelatedMatchers = append(unrelatedMatchers, &rewrittenM) 380 381 default: 382 unrelatedMatchers = append(unrelatedMatchers, m) 383 } 384 } 385 386 return matchedIDs, unrelatedMatchers 387 } 388 389 type addLabelsSeriesSet struct { 390 upstream storage.SeriesSet 391 labels labels.Labels 392 currSeries storage.Series 393 } 394 395 func (m *addLabelsSeriesSet) Next() bool { 396 m.currSeries = nil 397 return m.upstream.Next() 398 } 399 400 // At returns full series. Returned series should be iteratable even after Next is called. 401 func (m *addLabelsSeriesSet) At() storage.Series { 402 if m.currSeries == nil { 403 upstream := m.upstream.At() 404 m.currSeries = &addLabelsSeries{ 405 upstream: upstream, 406 labels: setLabelsRetainExisting(upstream.Labels(), m.labels...), 407 } 408 } 409 return m.currSeries 410 } 411 412 // The error that iteration as failed with. 413 // When an error occurs, set cannot continue to iterate. 414 func (m *addLabelsSeriesSet) Err() error { 415 return errors.Wrapf(m.upstream.Err(), "error querying %s", labelsToString(m.labels)) 416 } 417 418 // A collection of warnings for the whole set. 419 // Warnings could be return even iteration has not failed with error. 420 func (m *addLabelsSeriesSet) Warnings() storage.Warnings { 421 upstream := m.upstream.Warnings() 422 warnings := make(storage.Warnings, len(upstream)) 423 for pos := range upstream { 424 warnings[pos] = errors.Wrapf(upstream[pos], "warning querying %s", labelsToString(m.labels)) 425 } 426 return warnings 427 } 428 429 // rewrite label name to be more readable in error output 430 func rewriteLabelName(s string) string { 431 return strings.TrimRight(strings.TrimLeft(s, "_"), "_") 432 } 433 434 // this outputs a more readable error format 435 func labelsToString(labels labels.Labels) string { 436 parts := make([]string, len(labels)) 437 for pos, l := range labels { 438 parts[pos] = rewriteLabelName(l.Name) + " " + l.Value 439 } 440 return strings.Join(parts, ", ") 441 } 442 443 type addLabelsSeries struct { 444 upstream storage.Series 445 labels labels.Labels 446 } 447 448 // Labels returns the complete set of labels. For series it means all labels identifying the series. 449 func (a *addLabelsSeries) Labels() labels.Labels { 450 return a.labels 451 } 452 453 // Iterator returns a new, independent iterator of the data of the series. 454 func (a *addLabelsSeries) Iterator() chunkenc.Iterator { 455 return a.upstream.Iterator() 456 } 457 458 // this sets a label and preserves an existing value a new label prefixed with 459 // original_. It doesn't do this recursively. 460 func setLabelsRetainExisting(src labels.Labels, additionalLabels ...labels.Label) labels.Labels { 461 lb := labels.NewBuilder(src) 462 463 for _, additionalL := range additionalLabels { 464 if oldValue := src.Get(additionalL.Name); oldValue != "" { 465 lb.Set( 466 retainExistingPrefix+additionalL.Name, 467 oldValue, 468 ) 469 } 470 lb.Set(additionalL.Name, additionalL.Value) 471 } 472 473 return lb.Labels() 474 }