feat: Add robust abstraction for a sample "union" type.
#17925 opened on Jan 23, 2026
Description
Problem Space
Common complain when we logic around samples in Prometheus codebase is the fact we have so many different types and semi-repeated code with float vs (integer) histogram vs float histogram. Common examples:
switch {
case fh != nil:
isStale = value.IsStaleNaN(fh.Sum)
appErr = a.appendFloatHistogram(s, t, fh, opts.RejectOutOfOrder)
case h != nil:
isStale = value.IsStaleNaN(h.Sum)
appErr = a.appendHistogram(s, t, h, opts.RejectOutOfOrder)
default:
isStale = value.IsStaleNaN(v)
if isStale {
// v, h, fh represents sample value for each sample type.
// Callers MUST only provide one of the sample types (either v, h or fh).
// Implementations can detect the type of the sample with the following switch:
//
// switch {
// case fh != nil: It's a float histogram append.
// case h != nil: It's a histogram append.
// default: It's a float append.
// }
// TODO(bwplotka): We plan to experiment on using generics for complex sampleType, but do it after we unify interface (derisk) and before we add native summaries.
// ...
Append(ref SeriesRef, ls labels.Labels, st, t int64, v float64, h *histogram.Histogram, fh *histogram.FloatHistogram, opts AppendV2Options) (SeriesRef, error)
(TODO: Any other good examples of the issues I missed? cc @bboreham @beorn7 @krajorama ?)
Solving this would make Prometheus easier to maintain, extend. It might also be more reliable (e.g. accidental Append of 0, nil, nil could mean both "error" or 0 float sample append).
Notably it unblocks ideas like:
Acceptance Criteria
- We have a mechanism for robust way of generic logic per sample "type".
So e.g. appender could look like:
Append(ref SeriesRef, ls labels.Labels, st, t int64, v sample.Value, opts AppendV2Options) (SeriesRef, error)
- Adding another sample type is easier.
- Hot path of float sample is not significantly slower/alloc more memory
Ideas
Generics is an obvious choice. But in the past they incur an overhead on function calls. We might double check if it's now a bit more optimized or/and if the overhead is now acceptable. Having everywhere float, compositeType is also an option.
This problem could be equally solved with interfaces too.
There are also some fun projects that help with union-like logic: https://github.com/widmogrod/mkunion