checkRegexMatcherLimits validates regex matchers against configured limits to prevent expensive queries.
(ctx context.Context, userID string, db *userTSDB, matchers []*labels.Matcher, from, through int64)
| 2507 | |
| 2508 | // checkRegexMatcherLimits validates regex matchers against configured limits to prevent expensive queries. |
| 2509 | func (i *Ingester) checkRegexMatcherLimits(ctx context.Context, userID string, db *userTSDB, matchers []*labels.Matcher, from, through int64) error { |
| 2510 | // Collect all unoptimized regex matchers upfront |
| 2511 | var unoptimizedMatchers []*labels.Matcher |
| 2512 | for _, matcher := range matchers { |
| 2513 | if isRegexUnOptimized(matcher) { |
| 2514 | unoptimizedMatchers = append(unoptimizedMatchers, matcher) |
| 2515 | // Record pattern length metric |
| 2516 | if i.metrics.unoptimizedRegexPatternLength != nil { |
| 2517 | i.metrics.unoptimizedRegexPatternLength.Observe(float64(len(matcher.Value))) |
| 2518 | } |
| 2519 | } |
| 2520 | } |
| 2521 | |
| 2522 | if len(unoptimizedMatchers) == 0 { |
| 2523 | return nil |
| 2524 | } |
| 2525 | |
| 2526 | // Check pattern length limit if configured |
| 2527 | maxPatternLength := i.limits.MaxRegexPatternLength(userID) |
| 2528 | if maxPatternLength > 0 { |
| 2529 | for _, matcher := range unoptimizedMatchers { |
| 2530 | patternLength := len(matcher.Value) |
| 2531 | if patternLength > maxPatternLength { |
| 2532 | if i.metrics.unoptimizedRegexRejectedTotal != nil { |
| 2533 | i.metrics.unoptimizedRegexRejectedTotal.WithLabelValues(userID, "pattern_length").Inc() |
| 2534 | } |
| 2535 | return validation.LimitError(fmt.Sprintf( |
| 2536 | "regex pattern length %d exceeds limit %d for unoptimized regex matcher %q. Consider using a more specific pattern.", |
| 2537 | patternLength, maxPatternLength, matcher.String(), |
| 2538 | )) |
| 2539 | } |
| 2540 | } |
| 2541 | } |
| 2542 | |
| 2543 | // Query TSDB to collect cardinality and total value length metrics and check limits. |
| 2544 | labelQuerier, err := db.Querier(from, through) |
| 2545 | if err != nil { |
| 2546 | return err |
| 2547 | } |
| 2548 | defer labelQuerier.Close() |
| 2549 | |
| 2550 | maxCardinality := i.limits.MaxLabelCardinalityForUnoptimizedRegex(userID) |
| 2551 | maxTotalValueLength := i.limits.MaxTotalLabelValueLengthForUnoptimizedRegex(userID) |
| 2552 | |
| 2553 | for _, matcher := range unoptimizedMatchers { |
| 2554 | labelVals, _, err := labelQuerier.LabelValues(ctx, matcher.Name, nil) |
| 2555 | if err != nil { |
| 2556 | // If we can't get label values, skip this matcher and continue checking others |
| 2557 | continue |
| 2558 | } |
| 2559 | |
| 2560 | cardinality := len(labelVals) |
| 2561 | |
| 2562 | // Calculate total length of all values |
| 2563 | var totalValueLength int |
| 2564 | for _, val := range labelVals { |
| 2565 | totalValueLength += len(val) |
| 2566 | } |