histogram: Add Error type for all histogram errors

`histogram.Error` becomes the generic wrapper type for all histogram errors.
This makes it easier and less error prone when adding new errors to check if
an error is an histogram error as well as making it less error prone to convert
the errors.

This change the type of those specific sentinel errors from error to
`histogram.Error`, but it should almost never matter.
e.g., `errors.Is(err, ErrHistogram...)` would still work out of the box.

Signed-off-by: Laurent Dufresne <laurent.dufresne@grafana.com>
This commit is contained in:
Laurent Dufresne 2025-10-28 15:06:20 +01:00
parent 37418b5910
commit 7621eb772c
2 changed files with 30 additions and 36 deletions

View file

@ -28,33 +28,41 @@ const (
CustomBucketsSchema int32 = -53
)
type Error struct {
error
}
func (e Error) Unwrap() error {
return e.error
}
var (
ErrHistogramCountNotBigEnough = errors.New("histogram's observation count should be at least the number of observations found in the buckets")
ErrHistogramCountMismatch = errors.New("histogram's observation count should equal the number of observations found in the buckets (in absence of NaN)")
ErrHistogramNegativeCount = errors.New("histogram's observation count is negative")
ErrHistogramNegativeBucketCount = errors.New("histogram has a bucket whose observation count is negative")
ErrHistogramSpanNegativeOffset = errors.New("histogram has a span whose offset is negative")
ErrHistogramSpansBucketsMismatch = errors.New("histogram spans specify different number of buckets than provided")
ErrHistogramCustomBucketsMismatch = errors.New("histogram custom bounds are too few")
ErrHistogramCustomBucketsInvalid = errors.New("histogram custom bounds must be in strictly increasing order")
ErrHistogramCustomBucketsInfinite = errors.New("histogram custom bounds must be finite")
ErrHistogramCustomBucketsNaN = errors.New("histogram custom bounds must not be NaN")
ErrHistogramsIncompatibleSchema = errors.New("cannot apply this operation on histograms with a mix of exponential and custom bucket schemas")
ErrHistogramCustomBucketsZeroCount = errors.New("custom buckets: must have zero count of 0")
ErrHistogramCustomBucketsZeroThresh = errors.New("custom buckets: must have zero threshold of 0")
ErrHistogramCustomBucketsNegSpans = errors.New("custom buckets: must not have negative spans")
ErrHistogramCustomBucketsNegBuckets = errors.New("custom buckets: must not have negative buckets")
ErrHistogramExpSchemaCustomBounds = errors.New("histogram with exponential schema must not have custom bounds")
ErrHistogramsInvalidSchema = fmt.Errorf("histogram has an invalid schema, which must be between %d and %d for exponential buckets, or %d for custom buckets", ExponentialSchemaMin, ExponentialSchemaMax, CustomBucketsSchema)
ErrHistogramsUnknownSchema = fmt.Errorf("histogram has an unknown schema, which must be between %d and %d for exponential buckets, or %d for custom buckets", ExponentialSchemaMinReserved, ExponentialSchemaMaxReserved, CustomBucketsSchema)
ErrHistogramCountNotBigEnough = Error{error: errors.New("histogram's observation count should be at least the number of observations found in the buckets")}
ErrHistogramCountMismatch = Error{error: errors.New("histogram's observation count should equal the number of observations found in the buckets (in absence of NaN)")}
ErrHistogramNegativeCount = Error{error: errors.New("histogram's observation count is negative")}
ErrHistogramNegativeBucketCount = Error{error: errors.New("histogram has a bucket whose observation count is negative")}
ErrHistogramSpanNegativeOffset = Error{error: errors.New("histogram has a span whose offset is negative")}
ErrHistogramSpansBucketsMismatch = Error{error: errors.New("histogram spans specify different number of buckets than provided")}
ErrHistogramCustomBucketsMismatch = Error{error: errors.New("histogram custom bounds are too few")}
ErrHistogramCustomBucketsInvalid = Error{error: errors.New("histogram custom bounds must be in strictly increasing order")}
ErrHistogramCustomBucketsInfinite = Error{error: errors.New("histogram custom bounds must be finite")}
ErrHistogramCustomBucketsNaN = Error{error: errors.New("histogram custom bounds must not be NaN")}
ErrHistogramsIncompatibleSchema = Error{error: errors.New("cannot apply this operation on histograms with a mix of exponential and custom bucket schemas")}
ErrHistogramCustomBucketsZeroCount = Error{error: errors.New("custom buckets: must have zero count of 0")}
ErrHistogramCustomBucketsZeroThresh = Error{error: errors.New("custom buckets: must have zero threshold of 0")}
ErrHistogramCustomBucketsNegSpans = Error{error: errors.New("custom buckets: must not have negative spans")}
ErrHistogramCustomBucketsNegBuckets = Error{error: errors.New("custom buckets: must not have negative buckets")}
ErrHistogramExpSchemaCustomBounds = Error{error: errors.New("histogram with exponential schema must not have custom bounds")}
ErrHistogramsInvalidSchema = Error{error: fmt.Errorf("histogram has an invalid schema, which must be between %d and %d for exponential buckets, or %d for custom buckets", ExponentialSchemaMin, ExponentialSchemaMax, CustomBucketsSchema)}
ErrHistogramsUnknownSchema = Error{error: fmt.Errorf("histogram has an unknown schema, which must be between %d and %d for exponential buckets, or %d for custom buckets", ExponentialSchemaMinReserved, ExponentialSchemaMaxReserved, CustomBucketsSchema)}
)
func InvalidSchemaError(s int32) error {
return fmt.Errorf("%w, got schema %d", ErrHistogramsInvalidSchema, s)
return Error{error: fmt.Errorf("%w, got schema %d", ErrHistogramsInvalidSchema, s)}
}
func UnknownSchemaError(s int32) error {
return fmt.Errorf("%w, got schema %d", ErrHistogramsUnknownSchema, s)
return Error{error: fmt.Errorf("%w, got schema %d", ErrHistogramsUnknownSchema, s)}
}
func IsCustomBucketsSchema(s int32) bool {

View file

@ -89,22 +89,8 @@ func NewWriteHandler(logger *slog.Logger, reg prometheus.Registerer, appendable
// isHistogramValidationError checks if the error is a native histogram validation error.
func isHistogramValidationError(err error) bool {
// TODO: Consider adding single histogram error type instead of individual sentinel errors.
return errors.Is(err, histogram.ErrHistogramCountMismatch) ||
errors.Is(err, histogram.ErrHistogramCountNotBigEnough) ||
errors.Is(err, histogram.ErrHistogramNegativeCount) ||
errors.Is(err, histogram.ErrHistogramNegativeBucketCount) ||
errors.Is(err, histogram.ErrHistogramSpanNegativeOffset) ||
errors.Is(err, histogram.ErrHistogramSpansBucketsMismatch) ||
errors.Is(err, histogram.ErrHistogramCustomBucketsMismatch) ||
errors.Is(err, histogram.ErrHistogramCustomBucketsInvalid) ||
errors.Is(err, histogram.ErrHistogramCustomBucketsInfinite) ||
errors.Is(err, histogram.ErrHistogramCustomBucketsNaN) ||
errors.Is(err, histogram.ErrHistogramCustomBucketsZeroCount) ||
errors.Is(err, histogram.ErrHistogramCustomBucketsZeroThresh) ||
errors.Is(err, histogram.ErrHistogramCustomBucketsNegSpans) ||
errors.Is(err, histogram.ErrHistogramCustomBucketsNegBuckets) ||
errors.Is(err, histogram.ErrHistogramExpSchemaCustomBounds)
var e histogram.Error
return errors.As(err, &e)
}
// Store implements remoteapi.writeStorage interface.