| 374 | } |
| 375 | |
| 376 | func ValidateNativeHistogram(validateMetrics *ValidateMetrics, limits *Limits, userID string, ls []cortexpb.LabelAdapter, histogramSample cortexpb.Histogram) (cortexpb.Histogram, error) { |
| 377 | // sample size validation for native histogram |
| 378 | if limits.MaxNativeHistogramSampleSizeBytes > 0 && histogramSample.Size() > limits.MaxNativeHistogramSampleSizeBytes { |
| 379 | validateMetrics.DiscardedSamples.WithLabelValues(nativeHistogramSampleSizeBytesExceeded, userID).Inc() |
| 380 | return cortexpb.Histogram{}, newNativeHistogramSampleSizeBytesExceededError(ls, histogramSample.Size(), limits.MaxNativeHistogramSampleSizeBytes) |
| 381 | } |
| 382 | |
| 383 | // schema validation for native histogram |
| 384 | schema := histogramSample.Schema |
| 385 | isCustomBucketsSchema := schema == histogram.CustomBucketsSchema |
| 386 | isExponentialSchema := schema >= histogram.ExponentialSchemaMin && schema <= histogram.ExponentialSchemaMax |
| 387 | isValidSchema := isCustomBucketsSchema || isExponentialSchema |
| 388 | if !isValidSchema { |
| 389 | validateMetrics.DiscardedSamples.WithLabelValues(nativeHistogramInvalidSchema, userID).Inc() |
| 390 | return cortexpb.Histogram{}, newNativeHistogramSchemaInvalidError(ls, int(schema)) |
| 391 | } |
| 392 | |
| 393 | var ( |
| 394 | exceedLimit bool |
| 395 | ) |
| 396 | |
| 397 | if histogramSample.IsFloatHistogram() { |
| 398 | fh := cortexpb.FloatHistogramProtoToFloatHistogram(histogramSample) |
| 399 | if err := fh.Validate(); err != nil { |
| 400 | validateMetrics.DiscardedSamples.WithLabelValues(nativeHistogramInvalid, userID).Inc() |
| 401 | return cortexpb.Histogram{}, newNativeHistogramInvalidError(ls, err) |
| 402 | } |
| 403 | |
| 404 | // limit check |
| 405 | if limits.MaxNativeHistogramBuckets == 0 { |
| 406 | return histogramSample, nil |
| 407 | } |
| 408 | |
| 409 | // Custom bucket cannot reduce resolution |
| 410 | if isCustomBucketsSchema { |
| 411 | if len(fh.PositiveBuckets)+len(fh.NegativeBuckets) > limits.MaxNativeHistogramBuckets { |
| 412 | validateMetrics.DiscardedSamples.WithLabelValues(nativeHistogramBucketCountLimitExceeded, userID).Inc() |
| 413 | return cortexpb.Histogram{}, newHistogramBucketLimitExceededError(ls, limits.MaxNativeHistogramBuckets) |
| 414 | } |
| 415 | return histogramSample, nil |
| 416 | } |
| 417 | |
| 418 | exceedLimit = len(histogramSample.PositiveCounts)+len(histogramSample.NegativeCounts) > limits.MaxNativeHistogramBuckets |
| 419 | if !exceedLimit { |
| 420 | return histogramSample, nil |
| 421 | } |
| 422 | |
| 423 | // Exceed limit. |
| 424 | if schema <= histogram.ExponentialSchemaMin { |
| 425 | validateMetrics.DiscardedSamples.WithLabelValues(nativeHistogramBucketCountLimitExceeded, userID).Inc() |
| 426 | return cortexpb.Histogram{}, newHistogramBucketLimitExceededError(ls, limits.MaxNativeHistogramBuckets) |
| 427 | } |
| 428 | |
| 429 | oBuckets := len(fh.PositiveBuckets) + len(fh.NegativeBuckets) |
| 430 | for len(fh.PositiveBuckets)+len(fh.NegativeBuckets) > limits.MaxNativeHistogramBuckets { |
| 431 | if fh.Schema <= histogram.ExponentialSchemaMin { |
| 432 | validateMetrics.DiscardedSamples.WithLabelValues(nativeHistogramBucketCountLimitExceeded, userID).Inc() |
| 433 | return cortexpb.Histogram{}, newHistogramBucketLimitExceededError(ls, limits.MaxNativeHistogramBuckets) |