prw2: Move Remote Write 2.0 CT to be per Sample; Rename to ST (start timestamp) (#17411)

Relates to
https://github.com/prometheus/prometheus/issues/16944#issuecomment-3164760343

Signed-off-by: bwplotka <bwplotka@gmail.com>
(cherry picked from commit cefefc689766827a8c933e3181e9dd548656e71a)
This commit is contained in:
Bartlomiej Plotka 2025-11-17 15:59:40 +01:00 committed by bwplotka
parent a835937731
commit 3bcc88b053
7 changed files with 263 additions and 197 deletions

View File

@ -80,11 +80,6 @@ func (m *TimeSeries) OptimizedMarshalToSizedBuffer(dAtA []byte) (int, error) {
i -= len(m.XXX_unrecognized) i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized)
} }
if m.CreatedTimestamp != 0 {
i = encodeVarintTypes(dAtA, i, uint64(m.CreatedTimestamp))
i--
dAtA[i] = 0x30
}
{ {
size, err := m.Metadata.MarshalToSizedBuffer(dAtA[:i]) size, err := m.Metadata.MarshalToSizedBuffer(dAtA[:i])
if err != nil { if err != nil {

View File

@ -106,6 +106,8 @@ func (Histogram_ResetHint) EnumDescriptor() ([]byte, []int) {
// The canonical Content-Type request header value for this message is // The canonical Content-Type request header value for this message is
// "application/x-protobuf;proto=io.prometheus.write.v2.Request" // "application/x-protobuf;proto=io.prometheus.write.v2.Request"
// //
// Version: v2.0-rc.4
//
// NOTE: gogoproto options might change in future for this file, they // NOTE: gogoproto options might change in future for this file, they
// are not part of the spec proto (they only modify the generated Go code, not // are not part of the spec proto (they only modify the generated Go code, not
// the serialized message). See: https://github.com/prometheus/prometheus/issues/11908 // the serialized message). See: https://github.com/prometheus/prometheus/issues/11908
@ -181,7 +183,7 @@ type TimeSeries struct {
// //
// Note that there might be multiple TimeSeries objects in the same // Note that there might be multiple TimeSeries objects in the same
// Requests with the same labels e.g. for different exemplars, metadata // Requests with the same labels e.g. for different exemplars, metadata
// or created timestamp. // or start timestamp.
LabelsRefs []uint32 `protobuf:"varint,1,rep,packed,name=labels_refs,json=labelsRefs,proto3" json:"labels_refs,omitempty"` LabelsRefs []uint32 `protobuf:"varint,1,rep,packed,name=labels_refs,json=labelsRefs,proto3" json:"labels_refs,omitempty"`
// Timeseries messages can either specify samples or (native) histogram samples // Timeseries messages can either specify samples or (native) histogram samples
// (histogram field), but not both. For a typical sender (real-time metric // (histogram field), but not both. For a typical sender (real-time metric
@ -194,23 +196,6 @@ type TimeSeries struct {
Exemplars []Exemplar `protobuf:"bytes,4,rep,name=exemplars,proto3" json:"exemplars"` Exemplars []Exemplar `protobuf:"bytes,4,rep,name=exemplars,proto3" json:"exemplars"`
// metadata represents the metadata associated with the given series' samples. // metadata represents the metadata associated with the given series' samples.
Metadata Metadata `protobuf:"bytes,5,opt,name=metadata,proto3" json:"metadata"` Metadata Metadata `protobuf:"bytes,5,opt,name=metadata,proto3" json:"metadata"`
// created_timestamp represents an optional created timestamp associated with
// this series' samples in ms format, typically for counter or histogram type
// metrics. Created timestamp represents the time when the counter started
// counting (sometimes referred to as start timestamp), which can increase
// the accuracy of query results.
//
// Note that some receivers might require this and in return fail to
// ingest such samples within the Request.
//
// For Go, see github.com/prometheus/prometheus/model/timestamp/timestamp.go
// for conversion from/to time.Time to Prometheus timestamp.
//
// Note that the "optional" keyword is omitted due to
// https://cloud.google.com/apis/design/design_patterns.md#optional_primitive_fields
// Zero value means value not set. If you need to use exactly zero value for
// the timestamp, use 1 millisecond before or after.
CreatedTimestamp int64 `protobuf:"varint,6,opt,name=created_timestamp,json=createdTimestamp,proto3" json:"created_timestamp,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"` XXX_sizecache int32 `json:"-"`
@ -284,13 +269,6 @@ func (m *TimeSeries) GetMetadata() Metadata {
return Metadata{} return Metadata{}
} }
func (m *TimeSeries) GetCreatedTimestamp() int64 {
if m != nil {
return m.CreatedTimestamp
}
return 0
}
// Exemplar is an additional information attached to some series' samples. // Exemplar is an additional information attached to some series' samples.
// It is typically used to attach an example trace or request ID associated with // It is typically used to attach an example trace or request ID associated with
// the metric changes. // the metric changes.
@ -376,6 +354,26 @@ type Sample struct {
// For Go, see github.com/prometheus/prometheus/model/timestamp/timestamp.go // For Go, see github.com/prometheus/prometheus/model/timestamp/timestamp.go
// for conversion from/to time.Time to Prometheus timestamp. // for conversion from/to time.Time to Prometheus timestamp.
Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
// start_timestamp represents an optional start timestamp for the sample,
// in ms format. This information is typically used for counter, histogram (cumulative)
// or delta type metrics.
//
// For cumulative metrics, the start timestamp represents the time when the
// counter started counting (sometimes referred to as start timestamp), which
// can increase the accuracy of certain processing and query semantics (e.g. rates).
//
// Note:
// * That some receivers might require start timestamps for certain metric
// types; rejecting such samples within the Request as a result.
// * start timestamp is the same as "created timestamp" name Prometheus used in the past.
//
// For Go, see github.com/prometheus/prometheus/model/timestamp/timestamp.go
// for conversion from/to time.Time to Prometheus timestamp.
//
// Note that the "optional" keyword is omitted due to efficiency and consistency.
// Zero value means value not set. If you need to use exactly zero value for
// the timestamp, use 1 millisecond before or after.
StartTimestamp int64 `protobuf:"varint,3,opt,name=start_timestamp,json=startTimestamp,proto3" json:"start_timestamp,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"` XXX_sizecache int32 `json:"-"`
@ -428,6 +426,13 @@ func (m *Sample) GetTimestamp() int64 {
return 0 return 0
} }
func (m *Sample) GetStartTimestamp() int64 {
if m != nil {
return m.StartTimestamp
}
return 0
}
// Metadata represents the metadata associated with the given series' samples. // Metadata represents the metadata associated with the given series' samples.
type Metadata struct { type Metadata struct {
Type Metadata_MetricType `protobuf:"varint,1,opt,name=type,proto3,enum=io.prometheus.write.v2.Metadata_MetricType" json:"type,omitempty"` Type Metadata_MetricType `protobuf:"varint,1,opt,name=type,proto3,enum=io.prometheus.write.v2.Metadata_MetricType" json:"type,omitempty"`
@ -498,12 +503,11 @@ func (m *Metadata) GetUnitRef() uint32 {
return 0 return 0
} }
// A native histogram, also known as a sparse histogram. // A native histogram message, supporting
// Original design doc: // * sparse exponential bucketing, custom bucketing.
// https://docs.google.com/document/d/1cLNv3aufPZb3fNfaJgdaRBZsInZKKIHo9E6HinJVbpM/edit // * float or integer histograms.
// The appendix of this design doc also explains the concept of float //
// histograms. This Histogram message can represent both, the usual // See the full spec: https://prometheus.io/docs/specs/native_histograms/
// integer histogram as well as a float histogram.
type Histogram struct { type Histogram struct {
// Types that are valid to be assigned to Count: // Types that are valid to be assigned to Count:
// //
@ -582,6 +586,23 @@ type Histogram struct {
// The last element is not only the upper inclusive bound of the last regular // The last element is not only the upper inclusive bound of the last regular
// bucket, but implicitly the lower exclusive bound of the +Inf bucket. // bucket, but implicitly the lower exclusive bound of the +Inf bucket.
CustomValues []float64 `protobuf:"fixed64,16,rep,packed,name=custom_values,json=customValues,proto3" json:"custom_values,omitempty"` CustomValues []float64 `protobuf:"fixed64,16,rep,packed,name=custom_values,json=customValues,proto3" json:"custom_values,omitempty"`
// start_timestamp represents an optional start timestamp for the histogram sample,
// in ms format. The start timestamp represents the time when the histogram
// started counting, which can increase the accuracy of certain processing and
// query semantics (e.g. rates).
//
// Note:
// * That some receivers might require start timestamps for certain metric
// types; rejecting such samples within the Request as a result.
// * start timestamp is the same as "created timestamp" name Prometheus used in the past.
//
// For Go, see github.com/prometheus/prometheus/model/timestamp/timestamp.go
// for conversion from/to time.Time to Prometheus timestamp.
//
// Note that the "optional" keyword is omitted due to efficiency and consistency.
// Zero value means value not set. If you need to use exactly zero value for
// the timestamp, use 1 millisecond before or after.
StartTimestamp int64 `protobuf:"varint,17,opt,name=start_timestamp,json=startTimestamp,proto3" json:"start_timestamp,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"` XXX_sizecache int32 `json:"-"`
@ -774,6 +795,13 @@ func (m *Histogram) GetCustomValues() []float64 {
return nil return nil
} }
func (m *Histogram) GetStartTimestamp() int64 {
if m != nil {
return m.StartTimestamp
}
return 0
}
// XXX_OneofWrappers is for the internal use of the proto package. // XXX_OneofWrappers is for the internal use of the proto package.
func (*Histogram) XXX_OneofWrappers() []interface{} { func (*Histogram) XXX_OneofWrappers() []interface{} {
return []interface{}{ return []interface{}{
@ -861,65 +889,66 @@ func init() {
} }
var fileDescriptor_f139519efd9fa8d7 = []byte{ var fileDescriptor_f139519efd9fa8d7 = []byte{
// 926 bytes of a gzipped FileDescriptorProto // 931 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x55, 0x5d, 0x6f, 0xe3, 0x44, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x55, 0x5d, 0x6f, 0xe3, 0x44,
0x14, 0xed, 0xc4, 0x69, 0x3e, 0x6e, 0x9a, 0xac, 0x33, 0xb4, 0x5d, 0x6f, 0x81, 0x6c, 0xd6, 0x08, 0x14, 0xed, 0xc4, 0xf9, 0xbc, 0x69, 0xb2, 0xce, 0xd0, 0x76, 0xbd, 0x05, 0xb2, 0xd9, 0x20, 0x20,
0x88, 0x58, 0x29, 0x91, 0xc2, 0xeb, 0x0a, 0xd4, 0xb4, 0x6e, 0x93, 0x95, 0x92, 0xac, 0x26, 0x2e, 0x02, 0x29, 0x91, 0xc2, 0x2b, 0x02, 0x35, 0xad, 0xdb, 0xa4, 0x52, 0x92, 0xd5, 0xc4, 0x45, 0x2a,
0x52, 0x79, 0xb1, 0xdc, 0x64, 0x92, 0x58, 0xd8, 0xb1, 0xf1, 0x4c, 0x02, 0xe5, 0xf7, 0xf1, 0xb0, 0x2f, 0x96, 0x9b, 0x4e, 0x12, 0x0b, 0x3b, 0x36, 0x9e, 0x49, 0xa0, 0xfc, 0x40, 0xb4, 0x8f, 0xfc,
0x8f, 0xfc, 0x01, 0x10, 0xf4, 0x9d, 0xff, 0x80, 0x66, 0xfc, 0xd9, 0x42, 0xbb, 0xe2, 0x6d, 0xe6, 0x01, 0x10, 0xf4, 0x9d, 0xff, 0x80, 0x66, 0xfc, 0xd9, 0xd0, 0x76, 0xb5, 0x6f, 0x33, 0xe7, 0x9e,
0xdc, 0x73, 0xee, 0x3d, 0xb9, 0xbe, 0x77, 0x02, 0xba, 0xe3, 0xf7, 0x82, 0xd0, 0xf7, 0x28, 0x5f, 0x73, 0xef, 0xc9, 0xf5, 0xbd, 0x13, 0x68, 0xdb, 0x5e, 0xcf, 0x0f, 0x3c, 0x97, 0xf2, 0x15, 0xdd,
0xd3, 0x2d, 0xeb, 0xfd, 0x14, 0x3a, 0x9c, 0xf6, 0x76, 0xfd, 0x1e, 0xbf, 0x0d, 0x28, 0xeb, 0x06, 0xb0, 0xde, 0x2f, 0x81, 0xcd, 0x69, 0x6f, 0xdb, 0xef, 0xf1, 0x3b, 0x9f, 0xb2, 0xae, 0x1f, 0x78,
0xa1, 0xcf, 0x7d, 0x7c, 0xec, 0xf8, 0xdd, 0x8c, 0xd3, 0x95, 0x9c, 0xee, 0xae, 0x7f, 0x72, 0xb8, 0xdc, 0xc3, 0x47, 0xb6, 0xd7, 0x4d, 0x39, 0x5d, 0xc9, 0xe9, 0x6e, 0xfb, 0xc7, 0x07, 0x4b, 0x6f,
0xf2, 0x57, 0xbe, 0xa4, 0xf4, 0xc4, 0x29, 0x62, 0xeb, 0x0c, 0xca, 0x84, 0xfe, 0xb8, 0xa5, 0x8c, 0xe9, 0x49, 0x4a, 0x4f, 0x9c, 0x42, 0x76, 0x9b, 0x41, 0x89, 0xd0, 0x9f, 0x37, 0x94, 0x71, 0xac,
0x63, 0x0d, 0xca, 0xec, 0xd6, 0xbb, 0xf1, 0x5d, 0xa6, 0x15, 0xdb, 0x4a, 0xa7, 0x4a, 0x92, 0x2b, 0x41, 0x89, 0xdd, 0xb9, 0x37, 0x9e, 0xc3, 0xb4, 0x7c, 0x4b, 0xe9, 0x54, 0x48, 0x7c, 0xc5, 0x43,
0x1e, 0x02, 0x70, 0xc7, 0xa3, 0x8c, 0x86, 0x0e, 0x65, 0xda, 0x7e, 0x5b, 0xe9, 0xd4, 0xfa, 0x7a, 0x00, 0x6e, 0xbb, 0x94, 0xd1, 0xc0, 0xa6, 0x4c, 0x2b, 0xb4, 0x94, 0x4e, 0xb5, 0xdf, 0xee, 0x3e,
0xf7, 0xbf, 0xeb, 0x74, 0x4d, 0xc7, 0xa3, 0x33, 0xc9, 0x1c, 0x14, 0xdf, 0xff, 0xf1, 0x72, 0x8f, 0x5e, 0xa7, 0x6b, 0xd8, 0x2e, 0x9d, 0x49, 0xe6, 0x20, 0xff, 0xee, 0xaf, 0xd7, 0x7b, 0x24, 0xa3,
0xe4, 0xb4, 0x6f, 0x8b, 0x15, 0xa4, 0x16, 0xf5, 0xbf, 0x0b, 0x00, 0x19, 0x0d, 0xbf, 0x84, 0x9a, 0xbd, 0xcc, 0x97, 0x91, 0x9a, 0x6f, 0xff, 0x9e, 0x03, 0x48, 0x69, 0xf8, 0x35, 0x54, 0x1d, 0xeb,
0x6b, 0xdf, 0x50, 0x97, 0x59, 0x21, 0x5d, 0x32, 0x0d, 0xb5, 0x95, 0x4e, 0x9d, 0x40, 0x04, 0x11, 0x86, 0x3a, 0xcc, 0x0c, 0xe8, 0x82, 0x69, 0xa8, 0xa5, 0x74, 0x6a, 0x04, 0x42, 0x88, 0xd0, 0x05,
0xba, 0x64, 0xf8, 0x1b, 0x28, 0x33, 0xdb, 0x0b, 0x5c, 0xca, 0xb4, 0x82, 0x2c, 0xde, 0x7a, 0xac, 0xc3, 0xdf, 0x41, 0x89, 0x59, 0xae, 0xef, 0x50, 0xa6, 0xe5, 0x64, 0xf1, 0xe6, 0x53, 0xc5, 0x67,
0xf8, 0x4c, 0xd2, 0xe2, 0xc2, 0x89, 0x08, 0x5f, 0x02, 0xac, 0x1d, 0xc6, 0xfd, 0x55, 0x68, 0x7b, 0x92, 0x16, 0x15, 0x8e, 0x45, 0xf8, 0x02, 0x60, 0x65, 0x33, 0xee, 0x2d, 0x03, 0xcb, 0x65, 0x9a,
0x4c, 0x53, 0x64, 0x8a, 0x57, 0x8f, 0xa5, 0x18, 0x26, 0xcc, 0xc4, 0x7e, 0x26, 0xc5, 0xe7, 0x50, 0x22, 0x53, 0xbc, 0x79, 0x2a, 0xc5, 0x30, 0x66, 0xc6, 0xf6, 0x53, 0x29, 0x3e, 0x83, 0x0a, 0xfd,
0xa5, 0x3f, 0x53, 0x2f, 0x70, 0xed, 0x30, 0x6a, 0x52, 0xad, 0xdf, 0x7e, 0x2c, 0x8f, 0x11, 0x13, 0x95, 0xba, 0xbe, 0x63, 0x05, 0x61, 0x93, 0xaa, 0xfd, 0xd6, 0x53, 0x79, 0xf4, 0x88, 0x18, 0xa5,
0xe3, 0x34, 0x99, 0x10, 0x0f, 0xa0, 0xe2, 0x51, 0x6e, 0x2f, 0x6c, 0x6e, 0x6b, 0xfb, 0x6d, 0xf4, 0x49, 0x85, 0x78, 0x00, 0x65, 0x97, 0x72, 0xeb, 0xd6, 0xe2, 0x96, 0x56, 0x68, 0xa1, 0xe7, 0x92,
0x54, 0x92, 0x71, 0xcc, 0x8b, 0x93, 0xa4, 0x3a, 0xfc, 0x1a, 0x9a, 0xf3, 0x90, 0xda, 0x9c, 0x2e, 0x8c, 0x23, 0x5e, 0x94, 0x24, 0xd1, 0x5d, 0xe6, 0xcb, 0x45, 0xb5, 0xd4, 0x36, 0xa1, 0x1c, 0x97,
0x2c, 0xd9, 0x5e, 0x6e, 0x7b, 0x81, 0x56, 0x6a, 0xa3, 0x8e, 0x42, 0xd4, 0x38, 0x60, 0x26, 0xb8, 0x79, 0x7f, 0x17, 0x0f, 0xa0, 0xb0, 0xb5, 0x9c, 0x0d, 0xd5, 0x72, 0x2d, 0xd4, 0x41, 0x24, 0xbc,
0x6e, 0x41, 0x25, 0x71, 0xf3, 0xe1, 0x66, 0x1f, 0xc2, 0xfe, 0xce, 0x76, 0xb7, 0x54, 0x2b, 0xb4, 0xe0, 0x4f, 0xa0, 0x22, 0xbf, 0x0f, 0xb7, 0x5c, 0x5f, 0x53, 0x5a, 0xa8, 0xa3, 0x90, 0x14, 0x68,
0x51, 0x07, 0x91, 0xe8, 0x82, 0x3f, 0x81, 0x6a, 0x56, 0x47, 0x91, 0x75, 0x32, 0x40, 0x7f, 0x03, 0x53, 0x28, 0x86, 0x2d, 0x4d, 0xd5, 0xe8, 0x49, 0x75, 0x6e, 0x47, 0x8d, 0xbf, 0x84, 0x17, 0x8c,
0xa5, 0xa8, 0xf3, 0x99, 0x1a, 0x3d, 0xaa, 0x2e, 0x3c, 0x54, 0xff, 0x55, 0x80, 0x4a, 0xf2, 0x43, 0x5b, 0x01, 0x37, 0x77, 0x2b, 0xd4, 0x25, 0x6c, 0x24, 0x65, 0xfe, 0xc9, 0x41, 0x39, 0xfe, 0xa9,
0xf1, 0xb7, 0x50, 0x14, 0xd3, 0x2c, 0xf5, 0x8d, 0xfe, 0xeb, 0x0f, 0x35, 0x46, 0x1c, 0x42, 0x67, 0xf8, 0x7b, 0xc8, 0x8b, 0x79, 0x96, 0x85, 0xea, 0xfd, 0xaf, 0xdf, 0xd7, 0x1a, 0x71, 0x08, 0xec,
0x6e, 0xde, 0x06, 0x94, 0x48, 0x21, 0x7e, 0x01, 0x95, 0x35, 0x75, 0x03, 0xf1, 0xf3, 0xa4, 0xd1, 0xb9, 0x71, 0xe7, 0x53, 0x22, 0x85, 0xf8, 0x15, 0x94, 0x57, 0xd4, 0xf1, 0x45, 0x1f, 0x64, 0xbd,
0x3a, 0x29, 0x8b, 0x3b, 0xa1, 0x4b, 0x11, 0xda, 0x6e, 0x1c, 0x2e, 0x43, 0xc5, 0x28, 0x24, 0xee, 0x1a, 0x29, 0x89, 0x3b, 0xa1, 0x0b, 0x11, 0xda, 0xac, 0x6d, 0x2e, 0x43, 0xf9, 0x30, 0x24, 0xee,
0x84, 0x2e, 0xf5, 0xdf, 0x11, 0x40, 0x96, 0x0a, 0x7f, 0x0c, 0xcf, 0xc7, 0x86, 0x49, 0x46, 0x67, 0x84, 0x2e, 0xda, 0x7f, 0x22, 0x80, 0x34, 0x15, 0xfe, 0x18, 0x5e, 0x8e, 0x75, 0x83, 0x8c, 0x4e,
0x96, 0x79, 0xfd, 0xce, 0xb0, 0xae, 0x26, 0xb3, 0x77, 0xc6, 0xd9, 0xe8, 0x62, 0x64, 0x9c, 0xab, 0x4d, 0xe3, 0xfa, 0xad, 0x6e, 0x5e, 0x4d, 0x66, 0x6f, 0xf5, 0xd3, 0xd1, 0xf9, 0x48, 0x3f, 0x53,
0x7b, 0xf8, 0x39, 0x7c, 0x94, 0x0f, 0x9e, 0x4d, 0xaf, 0x26, 0xa6, 0x41, 0x54, 0x84, 0x8f, 0xa0, 0xf7, 0xf0, 0x4b, 0xf8, 0x28, 0x1b, 0x3c, 0x9d, 0x5e, 0x4d, 0x0c, 0x9d, 0xa8, 0x08, 0x1f, 0x42,
0x99, 0x0f, 0x5c, 0x9e, 0x5e, 0x5d, 0x1a, 0x6a, 0x01, 0xbf, 0x80, 0xa3, 0x3c, 0x3c, 0x1c, 0xcd, 0x23, 0x1b, 0xb8, 0x38, 0xb9, 0xba, 0xd0, 0xd5, 0x1c, 0x7e, 0x05, 0x87, 0x59, 0x78, 0x38, 0x9a,
0xcc, 0xe9, 0x25, 0x39, 0x1d, 0xab, 0x0a, 0x6e, 0xc1, 0xc9, 0xbf, 0x14, 0x59, 0xbc, 0xf8, 0xb0, 0x19, 0xd3, 0x0b, 0x72, 0x32, 0x56, 0x15, 0xdc, 0x84, 0xe3, 0xff, 0x29, 0xd2, 0x78, 0x7e, 0xb7,
0xd4, 0xec, 0x6a, 0x3c, 0x3e, 0x25, 0xd7, 0xea, 0x3e, 0x3e, 0x04, 0x35, 0x1f, 0x18, 0x4d, 0x2e, 0xd4, 0xec, 0x6a, 0x3c, 0x3e, 0x21, 0xd7, 0x6a, 0x01, 0x1f, 0x80, 0x9a, 0x0d, 0x8c, 0x26, 0xe7,
0xa6, 0x6a, 0x09, 0x6b, 0x70, 0x78, 0x8f, 0x6e, 0x9e, 0x9a, 0xc6, 0xcc, 0x30, 0xd5, 0xb2, 0xfe, 0x53, 0xb5, 0x88, 0x35, 0x38, 0x78, 0x40, 0x37, 0x4e, 0x0c, 0x7d, 0xa6, 0x1b, 0x6a, 0xa9, 0xfd,
0x6b, 0x09, 0xaa, 0xe9, 0x64, 0xe3, 0x4f, 0xa1, 0x3a, 0xf7, 0xb7, 0x1b, 0x6e, 0x39, 0x1b, 0x2e, 0x6f, 0x11, 0x2a, 0xc9, 0x6c, 0xe3, 0x4f, 0xa1, 0x32, 0xf7, 0x36, 0x6b, 0x6e, 0xda, 0x6b, 0x2e,
0x3b, 0x5d, 0x1c, 0xee, 0x91, 0x8a, 0x84, 0x46, 0x1b, 0x8e, 0x5f, 0x41, 0x2d, 0x0a, 0x2f, 0x5d, 0x3b, 0x9d, 0x1f, 0xee, 0x91, 0xb2, 0x84, 0x46, 0x6b, 0x8e, 0xdf, 0x40, 0x35, 0x0c, 0x2f, 0x1c,
0xdf, 0xe6, 0xd1, 0x20, 0x0c, 0xf7, 0x08, 0x48, 0xf0, 0x42, 0x60, 0x58, 0x05, 0x85, 0x6d, 0x3d, 0xcf, 0xe2, 0xe1, 0xc4, 0x0c, 0xf7, 0x08, 0x48, 0xf0, 0x5c, 0x60, 0x58, 0x05, 0x85, 0x6d, 0x5c,
0xd9, 0x60, 0x44, 0xc4, 0x11, 0x1f, 0x43, 0x89, 0xcd, 0xd7, 0xd4, 0xb3, 0x65, 0x6b, 0x9b, 0x24, 0xd9, 0x60, 0x44, 0xc4, 0x11, 0x1f, 0x41, 0x91, 0xcd, 0x57, 0xd4, 0xb5, 0x64, 0x6b, 0x1b, 0x24,
0xbe, 0xe1, 0xcf, 0xa1, 0xf1, 0x0b, 0x0d, 0x7d, 0x8b, 0xaf, 0x43, 0xca, 0xd6, 0xbe, 0xbb, 0x90, 0xba, 0xe1, 0xcf, 0xa1, 0xfe, 0x1b, 0x0d, 0x3c, 0x93, 0xaf, 0x02, 0xca, 0x56, 0x9e, 0x73, 0x2b,
0x33, 0x8f, 0x48, 0x5d, 0xa0, 0x66, 0x02, 0xe2, 0x2f, 0x62, 0x5a, 0xe6, 0xab, 0x24, 0x7d, 0x21, 0xa7, 0x1e, 0x91, 0x9a, 0x40, 0x8d, 0x18, 0xc4, 0x5f, 0x44, 0xb4, 0xd4, 0x57, 0x51, 0xfa, 0x42,
0x72, 0x20, 0xf0, 0xb3, 0xc4, 0xdb, 0x57, 0xa0, 0xe6, 0x78, 0x91, 0xc1, 0xb2, 0x34, 0x88, 0x48, 0x64, 0x5f, 0xe0, 0xa7, 0xb1, 0xb7, 0xaf, 0x40, 0xcd, 0xf0, 0x42, 0x83, 0x25, 0x69, 0x10, 0x91,
0x23, 0x65, 0x46, 0x26, 0xa7, 0xd0, 0xd8, 0xd0, 0x95, 0xcd, 0x9d, 0x1d, 0xb5, 0x58, 0x60, 0x6f, 0x7a, 0xc2, 0x0c, 0x4d, 0x4e, 0xa1, 0xbe, 0xa6, 0x4b, 0x8b, 0xdb, 0x5b, 0x6a, 0x32, 0xdf, 0x5a,
0x98, 0x56, 0x79, 0xfa, 0xed, 0x1a, 0x6c, 0xe7, 0x3f, 0x50, 0x3e, 0x0b, 0xec, 0x4d, 0xbc, 0x70, 0x33, 0xad, 0xfc, 0xfc, 0xeb, 0x35, 0xd8, 0xcc, 0x7f, 0xa2, 0x7c, 0xe6, 0x5b, 0xeb, 0x68, 0xe5,
0xf5, 0x44, 0x2f, 0x30, 0x86, 0xbf, 0x84, 0x67, 0x69, 0xc2, 0x05, 0x75, 0xb9, 0xcd, 0xb4, 0x6a, 0x6a, 0xb1, 0x5e, 0x60, 0x4c, 0x8c, 0x74, 0x92, 0xf0, 0x96, 0x3a, 0xdc, 0x62, 0x5a, 0xa5, 0xa5,
0x5b, 0xe9, 0x60, 0x92, 0xd6, 0x39, 0x97, 0xe8, 0x3d, 0xa2, 0x74, 0xca, 0x34, 0x68, 0x2b, 0x1d, 0x74, 0x30, 0x49, 0xea, 0x9c, 0x49, 0xf4, 0x01, 0x51, 0x3a, 0x65, 0x1a, 0xb4, 0x94, 0x0e, 0x4a,
0x94, 0x11, 0xa5, 0x4d, 0x26, 0x2c, 0x06, 0x3e, 0x73, 0x72, 0x16, 0x6b, 0xff, 0xd7, 0x62, 0xa2, 0x89, 0xd2, 0x26, 0x13, 0x16, 0x7d, 0x8f, 0xd9, 0x19, 0x8b, 0xd5, 0x0f, 0xb5, 0x18, 0xeb, 0x13,
0x4f, 0x2d, 0xa6, 0x09, 0x63, 0x8b, 0x07, 0x91, 0xc5, 0x04, 0xce, 0x2c, 0xa6, 0xc4, 0xd8, 0x62, 0x8b, 0x49, 0xc2, 0xc8, 0xe2, 0x7e, 0x68, 0x31, 0x86, 0x53, 0x8b, 0x09, 0x31, 0xb2, 0x58, 0x0b,
0x3d, 0xb2, 0x98, 0xc0, 0xb1, 0xc5, 0xb7, 0x00, 0x21, 0x65, 0x94, 0x5b, 0x6b, 0xf1, 0x55, 0x1a, 0x2d, 0xc6, 0x70, 0x64, 0xf1, 0x12, 0x20, 0xa0, 0x8c, 0x72, 0x73, 0x25, 0xbe, 0x4a, 0xfd, 0xf9,
0x4f, 0xef, 0x65, 0x3a, 0x63, 0x5d, 0x22, 0x34, 0x43, 0x67, 0xc3, 0x49, 0x35, 0x4c, 0x8e, 0xf7, 0xbd, 0x4c, 0x66, 0xac, 0x4b, 0x84, 0x66, 0x68, 0xaf, 0x39, 0xa9, 0x04, 0xf1, 0xf1, 0xe1, 0x8b,
0x1f, 0x82, 0x67, 0x0f, 0x1e, 0x02, 0xfc, 0x19, 0xd4, 0xe7, 0x5b, 0xc6, 0x7d, 0xcf, 0x92, 0xcf, 0xf1, 0x62, 0xf7, 0xc5, 0xf8, 0x0c, 0x6a, 0xf3, 0x0d, 0xe3, 0x9e, 0x6b, 0xca, 0xf7, 0x85, 0x69,
0x06, 0xd3, 0x54, 0x69, 0xe8, 0x20, 0x02, 0xbf, 0x93, 0x98, 0xbe, 0x80, 0x6a, 0x9a, 0x1a, 0x9f, 0xaa, 0x34, 0xb4, 0x1f, 0x82, 0x3f, 0x48, 0xec, 0xb1, 0x67, 0xa5, 0xf1, 0xe8, 0xb3, 0x72, 0x0b,
0xc0, 0x31, 0x11, 0x13, 0x6e, 0x0d, 0x47, 0x13, 0xf3, 0xc1, 0x9a, 0x62, 0x68, 0xe4, 0x62, 0xd7, 0x95, 0xc4, 0x03, 0x3e, 0x86, 0x23, 0x22, 0x56, 0xc1, 0x1c, 0x8e, 0x26, 0xc6, 0xce, 0x3e, 0x63,
0xc6, 0x4c, 0x45, 0xb8, 0x09, 0xf5, 0x1c, 0x36, 0x99, 0xaa, 0x05, 0xb1, 0x49, 0x39, 0x28, 0xda, 0xa8, 0x67, 0x62, 0xd7, 0xfa, 0x4c, 0x45, 0xb8, 0x01, 0xb5, 0x0c, 0x36, 0x99, 0xaa, 0x39, 0xb1,
0x59, 0x65, 0x50, 0x86, 0x7d, 0xd9, 0x94, 0xc1, 0x01, 0x40, 0x36, 0x6f, 0xfa, 0x1b, 0x80, 0xec, 0x72, 0x19, 0x28, 0x5c, 0x6e, 0x65, 0x50, 0x82, 0x82, 0xec, 0xde, 0x60, 0x1f, 0x20, 0x1d, 0xcc,
0x03, 0x88, 0x91, 0xf7, 0x97, 0x4b, 0x46, 0xa3, 0x1d, 0x6a, 0x92, 0xf8, 0x26, 0x70, 0x97, 0x6e, 0xf6, 0xb7, 0x00, 0xe9, 0x97, 0x12, 0xbb, 0xe1, 0x2d, 0x16, 0x8c, 0x86, 0xcb, 0xd6, 0x20, 0xd1,
0x56, 0x7c, 0x2d, 0x57, 0xa7, 0x4e, 0xe2, 0xdb, 0xe0, 0xe8, 0xfd, 0x5d, 0x0b, 0xfd, 0x76, 0xd7, 0x4d, 0xe0, 0x0e, 0x5d, 0x2f, 0xf9, 0x4a, 0xee, 0x58, 0x8d, 0x44, 0xb7, 0xc1, 0xe1, 0xbb, 0xfb,
0x42, 0x7f, 0xde, 0xb5, 0xd0, 0xf7, 0x65, 0xd9, 0xb4, 0x5d, 0xff, 0xa6, 0x24, 0xff, 0x8a, 0xbf, 0x26, 0xfa, 0xe3, 0xbe, 0x89, 0xfe, 0xbe, 0x6f, 0xa2, 0x1f, 0x4b, 0xb2, 0xbb, 0xdb, 0xfe, 0x4d,
0xfe, 0x27, 0x00, 0x00, 0xff, 0xff, 0x3e, 0xfc, 0x93, 0x1c, 0xde, 0x07, 0x00, 0x00, 0x51, 0xfe, 0x6b, 0x7f, 0xf3, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x62, 0x8f, 0x36, 0x4b, 0x09,
0x08, 0x00, 0x00,
} }
func (m *Request) Marshal() (dAtA []byte, err error) { func (m *Request) Marshal() (dAtA []byte, err error) {
@ -996,11 +1025,6 @@ func (m *TimeSeries) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i -= len(m.XXX_unrecognized) i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized)
} }
if m.CreatedTimestamp != 0 {
i = encodeVarintTypes(dAtA, i, uint64(m.CreatedTimestamp))
i--
dAtA[i] = 0x30
}
{ {
size, err := m.Metadata.MarshalToSizedBuffer(dAtA[:i]) size, err := m.Metadata.MarshalToSizedBuffer(dAtA[:i])
if err != nil { if err != nil {
@ -1154,6 +1178,11 @@ func (m *Sample) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i -= len(m.XXX_unrecognized) i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized)
} }
if m.StartTimestamp != 0 {
i = encodeVarintTypes(dAtA, i, uint64(m.StartTimestamp))
i--
dAtA[i] = 0x18
}
if m.Timestamp != 0 { if m.Timestamp != 0 {
i = encodeVarintTypes(dAtA, i, uint64(m.Timestamp)) i = encodeVarintTypes(dAtA, i, uint64(m.Timestamp))
i-- i--
@ -1234,6 +1263,13 @@ func (m *Histogram) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i -= len(m.XXX_unrecognized) i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized)
} }
if m.StartTimestamp != 0 {
i = encodeVarintTypes(dAtA, i, uint64(m.StartTimestamp))
i--
dAtA[i] = 0x1
i--
dAtA[i] = 0x88
}
if len(m.CustomValues) > 0 { if len(m.CustomValues) > 0 {
for iNdEx := len(m.CustomValues) - 1; iNdEx >= 0; iNdEx-- { for iNdEx := len(m.CustomValues) - 1; iNdEx >= 0; iNdEx-- {
f6 := math.Float64bits(float64(m.CustomValues[iNdEx])) f6 := math.Float64bits(float64(m.CustomValues[iNdEx]))
@ -1535,9 +1571,6 @@ func (m *TimeSeries) Size() (n int) {
} }
l = m.Metadata.Size() l = m.Metadata.Size()
n += 1 + l + sovTypes(uint64(l)) n += 1 + l + sovTypes(uint64(l))
if m.CreatedTimestamp != 0 {
n += 1 + sovTypes(uint64(m.CreatedTimestamp))
}
if m.XXX_unrecognized != nil { if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized) n += len(m.XXX_unrecognized)
} }
@ -1581,6 +1614,9 @@ func (m *Sample) Size() (n int) {
if m.Timestamp != 0 { if m.Timestamp != 0 {
n += 1 + sovTypes(uint64(m.Timestamp)) n += 1 + sovTypes(uint64(m.Timestamp))
} }
if m.StartTimestamp != 0 {
n += 1 + sovTypes(uint64(m.StartTimestamp))
}
if m.XXX_unrecognized != nil { if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized) n += len(m.XXX_unrecognized)
} }
@ -1670,6 +1706,9 @@ func (m *Histogram) Size() (n int) {
if len(m.CustomValues) > 0 { if len(m.CustomValues) > 0 {
n += 2 + sovTypes(uint64(len(m.CustomValues)*8)) + len(m.CustomValues)*8 n += 2 + sovTypes(uint64(len(m.CustomValues)*8)) + len(m.CustomValues)*8
} }
if m.StartTimestamp != 0 {
n += 2 + sovTypes(uint64(m.StartTimestamp))
}
if m.XXX_unrecognized != nil { if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized) n += len(m.XXX_unrecognized)
} }
@ -2093,25 +2132,6 @@ func (m *TimeSeries) Unmarshal(dAtA []byte) error {
return err return err
} }
iNdEx = postIndex iNdEx = postIndex
case 6:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field CreatedTimestamp", wireType)
}
m.CreatedTimestamp = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowTypes
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.CreatedTimestamp |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
default: default:
iNdEx = preIndex iNdEx = preIndex
skippy, err := skipTypes(dAtA[iNdEx:]) skippy, err := skipTypes(dAtA[iNdEx:])
@ -2350,6 +2370,25 @@ func (m *Sample) Unmarshal(dAtA []byte) error {
break break
} }
} }
case 3:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field StartTimestamp", wireType)
}
m.StartTimestamp = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowTypes
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.StartTimestamp |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
default: default:
iNdEx = preIndex iNdEx = preIndex
skippy, err := skipTypes(dAtA[iNdEx:]) skippy, err := skipTypes(dAtA[iNdEx:])
@ -3038,6 +3077,25 @@ func (m *Histogram) Unmarshal(dAtA []byte) error {
} else { } else {
return fmt.Errorf("proto: wrong wireType = %d for field CustomValues", wireType) return fmt.Errorf("proto: wrong wireType = %d for field CustomValues", wireType)
} }
case 17:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field StartTimestamp", wireType)
}
m.StartTimestamp = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowTypes
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.StartTimestamp |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
default: default:
iNdEx = preIndex iNdEx = preIndex
skippy, err := skipTypes(dAtA[iNdEx:]) skippy, err := skipTypes(dAtA[iNdEx:])

View File

@ -14,6 +14,7 @@
// NOTE: This file is also available on https://buf.build/prometheus/prometheus/docs/main:io.prometheus.write.v2 // NOTE: This file is also available on https://buf.build/prometheus/prometheus/docs/main:io.prometheus.write.v2
syntax = "proto3"; syntax = "proto3";
package io.prometheus.write.v2; package io.prometheus.write.v2;
option go_package = "writev2"; option go_package = "writev2";
@ -27,6 +28,8 @@ import "gogoproto/gogo.proto";
// The canonical Content-Type request header value for this message is // The canonical Content-Type request header value for this message is
// "application/x-protobuf;proto=io.prometheus.write.v2.Request" // "application/x-protobuf;proto=io.prometheus.write.v2.Request"
// //
// Version: v2.0-rc.4
//
// NOTE: gogoproto options might change in future for this file, they // NOTE: gogoproto options might change in future for this file, they
// are not part of the spec proto (they only modify the generated Go code, not // are not part of the spec proto (they only modify the generated Go code, not
// the serialized message). See: https://github.com/prometheus/prometheus/issues/11908 // the serialized message). See: https://github.com/prometheus/prometheus/issues/11908
@ -59,7 +62,7 @@ message TimeSeries {
// //
// Note that there might be multiple TimeSeries objects in the same // Note that there might be multiple TimeSeries objects in the same
// Requests with the same labels e.g. for different exemplars, metadata // Requests with the same labels e.g. for different exemplars, metadata
// or created timestamp. // or start timestamp.
repeated uint32 labels_refs = 1; repeated uint32 labels_refs = 1;
// Timeseries messages can either specify samples or (native) histogram samples // Timeseries messages can either specify samples or (native) histogram samples
@ -76,23 +79,9 @@ message TimeSeries {
// metadata represents the metadata associated with the given series' samples. // metadata represents the metadata associated with the given series' samples.
Metadata metadata = 5 [(gogoproto.nullable) = false]; Metadata metadata = 5 [(gogoproto.nullable) = false];
// created_timestamp represents an optional created timestamp associated with // This field is reserved for backward compatibility with the deprecated fields;
// this series' samples in ms format, typically for counter or histogram type // previously present in the experimental remote write period.
// metrics. Created timestamp represents the time when the counter started reserved 6;
// counting (sometimes referred to as start timestamp), which can increase
// the accuracy of query results.
//
// Note that some receivers might require this and in return fail to
// ingest such samples within the Request.
//
// For Go, see github.com/prometheus/prometheus/model/timestamp/timestamp.go
// for conversion from/to time.Time to Prometheus timestamp.
//
// Note that the "optional" keyword is omitted due to
// https://cloud.google.com/apis/design/design_patterns.md#optional_primitive_fields
// Zero value means value not set. If you need to use exactly zero value for
// the timestamp, use 1 millisecond before or after.
int64 created_timestamp = 6;
} }
// Exemplar is an additional information attached to some series' samples. // Exemplar is an additional information attached to some series' samples.
@ -123,6 +112,26 @@ message Sample {
// For Go, see github.com/prometheus/prometheus/model/timestamp/timestamp.go // For Go, see github.com/prometheus/prometheus/model/timestamp/timestamp.go
// for conversion from/to time.Time to Prometheus timestamp. // for conversion from/to time.Time to Prometheus timestamp.
int64 timestamp = 2; int64 timestamp = 2;
// start_timestamp represents an optional start timestamp for the sample,
// in ms format. This information is typically used for counter, histogram (cumulative)
// or delta type metrics.
//
// For cumulative metrics, the start timestamp represents the time when the
// counter started counting (sometimes referred to as start timestamp), which
// can increase the accuracy of certain processing and query semantics (e.g. rates).
//
// Note:
// * That some receivers might require start timestamps for certain metric
// types; rejecting such samples within the Request as a result.
// * start timestamp is the same as "created timestamp" name Prometheus used in the past.
//
// For Go, see github.com/prometheus/prometheus/model/timestamp/timestamp.go
// for conversion from/to time.Time to Prometheus timestamp.
//
// Note that the "optional" keyword is omitted due to efficiency and consistency.
// Zero value means value not set. If you need to use exactly zero value for
// the timestamp, use 1 millisecond before or after.
int64 start_timestamp = 3;
} }
// Metadata represents the metadata associated with the given series' samples. // Metadata represents the metadata associated with the given series' samples.
@ -148,12 +157,11 @@ message Metadata {
uint32 unit_ref = 4; uint32 unit_ref = 4;
} }
// A native histogram, also known as a sparse histogram. // A native histogram message, supporting
// Original design doc: // * sparse exponential bucketing, custom bucketing.
// https://docs.google.com/document/d/1cLNv3aufPZb3fNfaJgdaRBZsInZKKIHo9E6HinJVbpM/edit // * float or integer histograms.
// The appendix of this design doc also explains the concept of float //
// histograms. This Histogram message can represent both, the usual // See the full spec: https://prometheus.io/docs/specs/native_histograms/
// integer histogram as well as a float histogram.
message Histogram { message Histogram {
enum ResetHint { enum ResetHint {
RESET_HINT_UNSPECIFIED = 0; // Need to test for a counter reset explicitly. RESET_HINT_UNSPECIFIED = 0; // Need to test for a counter reset explicitly.
@ -242,6 +250,24 @@ message Histogram {
// The last element is not only the upper inclusive bound of the last regular // The last element is not only the upper inclusive bound of the last regular
// bucket, but implicitly the lower exclusive bound of the +Inf bucket. // bucket, but implicitly the lower exclusive bound of the +Inf bucket.
repeated double custom_values = 16; repeated double custom_values = 16;
// start_timestamp represents an optional start timestamp for the histogram sample,
// in ms format. The start timestamp represents the time when the histogram
// started counting, which can increase the accuracy of certain processing and
// query semantics (e.g. rates).
//
// Note:
// * That some receivers might require start timestamps for certain metric
// types; rejecting such samples within the Request as a result.
// * start timestamp is the same as "created timestamp" name Prometheus used in the past.
//
// For Go, see github.com/prometheus/prometheus/model/timestamp/timestamp.go
// for conversion from/to time.Time to Prometheus timestamp.
//
// Note that the "optional" keyword is omitted due to efficiency and consistency.
// Zero value means value not set. If you need to use exactly zero value for
// the timestamp, use 1 millisecond before or after.
int64 start_timestamp = 17;
} }
// A BucketSpan defines a number of consecutive buckets with their // A BucketSpan defines a number of consecutive buckets with their

View File

@ -114,7 +114,7 @@ var (
HelpRef: 15, // Symbolized writeV2RequestSeries1Metadata.Help. HelpRef: 15, // Symbolized writeV2RequestSeries1Metadata.Help.
UnitRef: 16, // Symbolized writeV2RequestSeries1Metadata.Unit. UnitRef: 16, // Symbolized writeV2RequestSeries1Metadata.Unit.
}, },
Samples: []writev2.Sample{{Value: 1, Timestamp: 10}}, Samples: []writev2.Sample{{Value: 1, Timestamp: 10, StartTimestamp: 1}}, // ST needs to be lower than the sample's timestamp.
Exemplars: []writev2.Exemplar{{LabelsRefs: []uint32{11, 12}, Value: 1, Timestamp: 10}}, Exemplars: []writev2.Exemplar{{LabelsRefs: []uint32{11, 12}, Value: 1, Timestamp: 10}},
Histograms: []writev2.Histogram{ Histograms: []writev2.Histogram{
writev2.FromIntHistogram(10, &testHistogram), writev2.FromIntHistogram(10, &testHistogram),
@ -122,7 +122,6 @@ var (
writev2.FromIntHistogram(30, &testHistogramCustomBuckets), writev2.FromIntHistogram(30, &testHistogramCustomBuckets),
writev2.FromFloatHistogram(40, testHistogramCustomBuckets.ToFloat(nil)), writev2.FromFloatHistogram(40, testHistogramCustomBuckets.ToFloat(nil)),
}, },
CreatedTimestamp: 1, // CT needs to be lower than the sample's timestamp.
}, },
{ {
LabelsRefs: []uint32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, // Same series as first. LabelsRefs: []uint32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, // Same series as first.
@ -182,7 +181,7 @@ func TestWriteV2RequestFixture(t *testing.T) {
HelpRef: st.Symbolize(writeV2RequestSeries1Metadata.Help), HelpRef: st.Symbolize(writeV2RequestSeries1Metadata.Help),
UnitRef: st.Symbolize(writeV2RequestSeries1Metadata.Unit), UnitRef: st.Symbolize(writeV2RequestSeries1Metadata.Unit),
}, },
Samples: []writev2.Sample{{Value: 1, Timestamp: 10}}, Samples: []writev2.Sample{{Value: 1, Timestamp: 10, StartTimestamp: 1}},
Exemplars: []writev2.Exemplar{{LabelsRefs: exemplar1LabelRefs, Value: 1, Timestamp: 10}}, Exemplars: []writev2.Exemplar{{LabelsRefs: exemplar1LabelRefs, Value: 1, Timestamp: 10}},
Histograms: []writev2.Histogram{ Histograms: []writev2.Histogram{
writev2.FromIntHistogram(10, &testHistogram), writev2.FromIntHistogram(10, &testHistogram),
@ -190,7 +189,6 @@ func TestWriteV2RequestFixture(t *testing.T) {
writev2.FromIntHistogram(30, &testHistogramCustomBuckets), writev2.FromIntHistogram(30, &testHistogramCustomBuckets),
writev2.FromFloatHistogram(40, testHistogramCustomBuckets.ToFloat(nil)), writev2.FromFloatHistogram(40, testHistogramCustomBuckets.ToFloat(nil)),
}, },
CreatedTimestamp: 1,
}, },
{ {
LabelsRefs: labelRefs, LabelsRefs: labelRefs,

View File

@ -106,7 +106,7 @@ func exponentialToNativeHistogram(p pmetric.ExponentialHistogramDataPoint, tempo
// Sending a sample that triggers counter reset but with ResetHint==NO // Sending a sample that triggers counter reset but with ResetHint==NO
// would lead to Prometheus panic as it does not double check the hint. // would lead to Prometheus panic as it does not double check the hint.
// Thus we're explicitly saying UNKNOWN here, which is always safe. // Thus we're explicitly saying UNKNOWN here, which is always safe.
// TODO: using created time stamp should be accurate, but we // TODO: using start timestamp should be accurate, but we
// need to know here if it was used for the detection. // need to know here if it was used for the detection.
// Ref: https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/28663#issuecomment-1810577303 // Ref: https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/28663#issuecomment-1810577303
// Counter reset detection in Prometheus: https://github.com/prometheus/prometheus/blob/f997c72f294c0f18ca13fa06d51889af04135195/tsdb/chunkenc/histogram.go#L232 // Counter reset detection in Prometheus: https://github.com/prometheus/prometheus/blob/f997c72f294c0f18ca13fa06d51889af04135195/tsdb/chunkenc/histogram.go#L232
@ -312,7 +312,7 @@ func explicitHistogramToCustomBucketsHistogram(p pmetric.HistogramDataPoint, tem
// Sending a sample that triggers counter reset but with ResetHint==NO // Sending a sample that triggers counter reset but with ResetHint==NO
// would lead to Prometheus panic as it does not double check the hint. // would lead to Prometheus panic as it does not double check the hint.
// Thus we're explicitly saying UNKNOWN here, which is always safe. // Thus we're explicitly saying UNKNOWN here, which is always safe.
// TODO: using created time stamp should be accurate, but we // TODO: using start timestamp should be accurate, but we
// need to know here if it was used for the detection. // need to know here if it was used for the detection.
// Ref: https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/28663#issuecomment-1810577303 // Ref: https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/28663#issuecomment-1810577303
// Counter reset detection in Prometheus: https://github.com/prometheus/prometheus/blob/f997c72f294c0f18ca13fa06d51889af04135195/tsdb/chunkenc/histogram.go#L232 // Counter reset detection in Prometheus: https://github.com/prometheus/prometheus/blob/f997c72f294c0f18ca13fa06d51889af04135195/tsdb/chunkenc/histogram.go#L232

View File

@ -353,20 +353,18 @@ func (h *writeHandler) appendV2(app storage.Appender, req *writev2.Request, rs *
allSamplesSoFar := rs.AllSamples() allSamplesSoFar := rs.AllSamples()
var ref storage.SeriesRef var ref storage.SeriesRef
// Samples.
if h.ingestCTZeroSample && len(ts.Samples) > 0 && ts.Samples[0].Timestamp != 0 && ts.CreatedTimestamp != 0 {
// CT only needs to be ingested for the first sample, it will be considered
// out of order for the rest.
ref, err = app.AppendCTZeroSample(ref, ls, ts.Samples[0].Timestamp, ts.CreatedTimestamp)
if err != nil && !errors.Is(err, storage.ErrOutOfOrderCT) {
// Even for the first sample OOO is a common scenario because
// we can't tell if a CT was already ingested in a previous request.
// We ignore the error.
h.logger.Debug("Error when appending CT in remote write request", "err", err, "series", ls.String(), "created_timestamp", ts.CreatedTimestamp, "timestamp", ts.Samples[0].Timestamp)
}
}
for _, s := range ts.Samples { for _, s := range ts.Samples {
if h.ingestCTZeroSample && s.StartTimestamp != 0 && s.Timestamp != 0 {
ref, err = app.AppendCTZeroSample(ref, ls, s.Timestamp, s.StartTimestamp)
// We treat OOO errors specially as it's a common scenario given:
// * We can't tell if ST was already ingested in a previous request.
// * We don't check if ST changed for stream of samples (we typically have one though),
// as it's checked in the AppendSTZeroSample reliably.
if err != nil && !errors.Is(err, storage.ErrOutOfOrderCT) {
h.logger.Debug("Error when appending ST from remote write request", "err", err, "series", ls.String(), "start_timestamp", s.StartTimestamp, "timestamp", s.Timestamp)
}
}
ref, err = app.Append(ref, ls, s.GetTimestamp(), s.GetValue()) ref, err = app.Append(ref, ls, s.GetTimestamp(), s.GetValue())
if err == nil { if err == nil {
rs.Samples++ rs.Samples++
@ -387,15 +385,14 @@ func (h *writeHandler) appendV2(app storage.Appender, req *writev2.Request, rs *
// Native Histograms. // Native Histograms.
for _, hp := range ts.Histograms { for _, hp := range ts.Histograms {
if h.ingestCTZeroSample && hp.Timestamp != 0 && ts.CreatedTimestamp != 0 { if h.ingestCTZeroSample && hp.StartTimestamp != 0 && hp.Timestamp != 0 {
// Differently from samples, we need to handle CT for each histogram instead of just the first one. ref, err = h.handleHistogramZeroSample(app, ref, ls, hp, hp.StartTimestamp)
// This is because histograms and float histograms are stored separately, even if they have the same labels. // We treat OOO errors specially as it's a common scenario given:
ref, err = h.handleHistogramZeroSample(app, ref, ls, hp, ts.CreatedTimestamp) // * We can't tell if ST was already ingested in a previous request.
// * We don't check if ST changed for stream of samples (we typically have one though),
// as it's checked in the ingestSTZeroSample reliably.
if err != nil && !errors.Is(err, storage.ErrOutOfOrderCT) { if err != nil && !errors.Is(err, storage.ErrOutOfOrderCT) {
// Even for the first sample OOO is a common scenario because h.logger.Debug("Error when appending ST from remote write request", "err", err, "series", ls.String(), "start_timestamp", hp.StartTimestamp, "timestamp", hp.Timestamp)
// we can't tell if a CT was already ingested in a previous request.
// We ignore the error.
h.logger.Debug("Error when appending CT in remote write request", "err", err, "series", ls.String(), "created_timestamp", ts.CreatedTimestamp, "timestamp", hp.Timestamp)
} }
} }
if hp.IsFloatHistogram() { if hp.IsFloatHistogram() {
@ -474,14 +471,14 @@ func (h *writeHandler) appendV2(app storage.Appender, req *writev2.Request, rs *
return samplesWithoutMetadata, http.StatusBadRequest, errors.Join(badRequestErrs...) return samplesWithoutMetadata, http.StatusBadRequest, errors.Join(badRequestErrs...)
} }
// handleHistogramZeroSample appends CT as a zero-value sample with CT value as the sample timestamp. // handleHistogramZeroSample appends ST as a zero-value sample with st value as the sample timestamp.
// It doesn't return errors in case of out of order CT. // It doesn't return errors in case of out of order ST.
func (*writeHandler) handleHistogramZeroSample(app storage.Appender, ref storage.SeriesRef, l labels.Labels, hist writev2.Histogram, ct int64) (storage.SeriesRef, error) { func (*writeHandler) handleHistogramZeroSample(app storage.Appender, ref storage.SeriesRef, l labels.Labels, hist writev2.Histogram, st int64) (storage.SeriesRef, error) {
var err error var err error
if hist.IsFloatHistogram() { if hist.IsFloatHistogram() {
ref, err = app.AppendHistogramCTZeroSample(ref, l, hist.Timestamp, ct, nil, hist.ToFloatHistogram()) ref, err = app.AppendHistogramCTZeroSample(ref, l, hist.Timestamp, st, nil, hist.ToFloatHistogram())
} else { } else {
ref, err = app.AppendHistogramCTZeroSample(ref, l, hist.Timestamp, ct, hist.ToIntHistogram(), nil) ref, err = app.AppendHistogramCTZeroSample(ref, l, hist.Timestamp, st, hist.ToIntHistogram(), nil)
} }
return ref, err return ref, err
} }

View File

@ -752,14 +752,12 @@ func TestRemoteWriteHandler_V2Message(t *testing.T) {
i, j, k, m int i, j, k, m int
) )
for _, ts := range writeV2RequestFixture.Timeseries { for _, ts := range writeV2RequestFixture.Timeseries {
zeroHistogramIngested := false
zeroFloatHistogramIngested := false
ls, err := ts.ToLabels(&b, writeV2RequestFixture.Symbols) ls, err := ts.ToLabels(&b, writeV2RequestFixture.Symbols)
require.NoError(t, err) require.NoError(t, err)
for _, s := range ts.Samples { for _, s := range ts.Samples {
if ts.CreatedTimestamp != 0 && tc.ingestCTZeroSample { if s.StartTimestamp != 0 && tc.ingestCTZeroSample {
requireEqual(t, mockSample{ls, ts.CreatedTimestamp, 0}, appendable.samples[i]) requireEqual(t, mockSample{ls, s.StartTimestamp, 0}, appendable.samples[i])
i++ i++
} }
requireEqual(t, mockSample{ls, s.Timestamp, s.Value}, appendable.samples[i]) requireEqual(t, mockSample{ls, s.Timestamp, s.Value}, appendable.samples[i])
@ -768,27 +766,21 @@ func TestRemoteWriteHandler_V2Message(t *testing.T) {
for _, hp := range ts.Histograms { for _, hp := range ts.Histograms {
if hp.IsFloatHistogram() { if hp.IsFloatHistogram() {
fh := hp.ToFloatHistogram() fh := hp.ToFloatHistogram()
if !zeroFloatHistogramIngested && ts.CreatedTimestamp != 0 && tc.ingestCTZeroSample { if hp.StartTimestamp != 0 && tc.ingestCTZeroSample {
requireEqual(t, mockHistogram{ls, ts.CreatedTimestamp, nil, &histogram.FloatHistogram{}}, appendable.histograms[k]) requireEqual(t, mockHistogram{ls, hp.StartTimestamp, nil, &histogram.FloatHistogram{}}, appendable.histograms[k])
k++ k++
zeroFloatHistogramIngested = true
} }
requireEqual(t, mockHistogram{ls, hp.Timestamp, nil, fh}, appendable.histograms[k]) requireEqual(t, mockHistogram{ls, hp.Timestamp, nil, fh}, appendable.histograms[k])
} else { } else {
h := hp.ToIntHistogram() h := hp.ToIntHistogram()
if !zeroHistogramIngested && ts.CreatedTimestamp != 0 && tc.ingestCTZeroSample { if hp.StartTimestamp != 0 && tc.ingestCTZeroSample {
requireEqual(t, mockHistogram{ls, ts.CreatedTimestamp, &histogram.Histogram{}, nil}, appendable.histograms[k]) requireEqual(t, mockHistogram{ls, hp.StartTimestamp, &histogram.Histogram{}, nil}, appendable.histograms[k])
k++ k++
zeroHistogramIngested = true
} }
requireEqual(t, mockHistogram{ls, hp.Timestamp, h, nil}, appendable.histograms[k]) requireEqual(t, mockHistogram{ls, hp.Timestamp, h, nil}, appendable.histograms[k])
} }
k++ k++
} }
if ts.CreatedTimestamp != 0 && tc.ingestCTZeroSample {
require.True(t, zeroHistogramIngested)
require.True(t, zeroFloatHistogramIngested)
}
if tc.appendExemplarErr == nil { if tc.appendExemplarErr == nil {
for _, e := range ts.Exemplars { for _, e := range ts.Exemplars {
ex, err := e.ToExemplar(&b, writeV2RequestFixture.Symbols) ex, err := e.ToExemplar(&b, writeV2RequestFixture.Symbols)