Skip to content

Commit 846591a

Browse files
authored
Merge pull request #417 from zenador/sparsehistograms
Add histogram vectors to model
2 parents ccc2474 + 691e814 commit 846591a

File tree

7 files changed

+1575
-344
lines changed

7 files changed

+1575
-344
lines changed

model/value.go

Lines changed: 116 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,12 @@ package model
1616
import (
1717
"encoding/json"
1818
"fmt"
19-
"math"
2019
"sort"
2120
"strconv"
2221
"strings"
2322
)
2423

2524
var (
26-
// ZeroSamplePair is the pseudo zero-value of SamplePair used to signal a
27-
// non-existing sample pair. It is a SamplePair with timestamp Earliest and
28-
// value 0.0. Note that the natural zero value of SamplePair has a timestamp
29-
// of 0, which is possible to appear in a real SamplePair and thus not
30-
// suitable to signal a non-existing SamplePair.
31-
ZeroSamplePair = SamplePair{Timestamp: Earliest}
32-
3325
// ZeroSample is the pseudo zero-value of Sample used to signal a
3426
// non-existing sample. It is a Sample with timestamp Earliest, value 0.0,
3527
// and metric nil. Note that the natural zero value of Sample has a timestamp
@@ -38,82 +30,14 @@ var (
3830
ZeroSample = Sample{Timestamp: Earliest}
3931
)
4032

41-
// A SampleValue is a representation of a value for a given sample at a given
42-
// time.
43-
type SampleValue float64
44-
45-
// MarshalJSON implements json.Marshaler.
46-
func (v SampleValue) MarshalJSON() ([]byte, error) {
47-
return json.Marshal(v.String())
48-
}
49-
50-
// UnmarshalJSON implements json.Unmarshaler.
51-
func (v *SampleValue) UnmarshalJSON(b []byte) error {
52-
if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' {
53-
return fmt.Errorf("sample value must be a quoted string")
54-
}
55-
f, err := strconv.ParseFloat(string(b[1:len(b)-1]), 64)
56-
if err != nil {
57-
return err
58-
}
59-
*v = SampleValue(f)
60-
return nil
61-
}
62-
63-
// Equal returns true if the value of v and o is equal or if both are NaN. Note
64-
// that v==o is false if both are NaN. If you want the conventional float
65-
// behavior, use == to compare two SampleValues.
66-
func (v SampleValue) Equal(o SampleValue) bool {
67-
if v == o {
68-
return true
69-
}
70-
return math.IsNaN(float64(v)) && math.IsNaN(float64(o))
71-
}
72-
73-
func (v SampleValue) String() string {
74-
return strconv.FormatFloat(float64(v), 'f', -1, 64)
75-
}
76-
77-
// SamplePair pairs a SampleValue with a Timestamp.
78-
type SamplePair struct {
79-
Timestamp Time
80-
Value SampleValue
81-
}
82-
83-
// MarshalJSON implements json.Marshaler.
84-
func (s SamplePair) MarshalJSON() ([]byte, error) {
85-
t, err := json.Marshal(s.Timestamp)
86-
if err != nil {
87-
return nil, err
88-
}
89-
v, err := json.Marshal(s.Value)
90-
if err != nil {
91-
return nil, err
92-
}
93-
return []byte(fmt.Sprintf("[%s,%s]", t, v)), nil
94-
}
95-
96-
// UnmarshalJSON implements json.Unmarshaler.
97-
func (s *SamplePair) UnmarshalJSON(b []byte) error {
98-
v := [...]json.Unmarshaler{&s.Timestamp, &s.Value}
99-
return json.Unmarshal(b, &v)
100-
}
101-
102-
// Equal returns true if this SamplePair and o have equal Values and equal
103-
// Timestamps. The semantics of Value equality is defined by SampleValue.Equal.
104-
func (s *SamplePair) Equal(o *SamplePair) bool {
105-
return s == o || (s.Value.Equal(o.Value) && s.Timestamp.Equal(o.Timestamp))
106-
}
107-
108-
func (s SamplePair) String() string {
109-
return fmt.Sprintf("%s @[%s]", s.Value, s.Timestamp)
110-
}
111-
112-
// Sample is a sample pair associated with a metric.
33+
// Sample is a sample pair associated with a metric. A single sample must either
34+
// define Value or Histogram but not both. Histogram == nil implies the Value
35+
// field is used, otherwise it should be ignored.
11336
type Sample struct {
114-
Metric Metric `json:"metric"`
115-
Value SampleValue `json:"value"`
116-
Timestamp Time `json:"timestamp"`
37+
Metric Metric `json:"metric"`
38+
Value SampleValue `json:"value"`
39+
Timestamp Time `json:"timestamp"`
40+
Histogram *SampleHistogram `json:"histogram"`
11741
}
11842

11943
// Equal compares first the metrics, then the timestamp, then the value. The
@@ -129,11 +53,19 @@ func (s *Sample) Equal(o *Sample) bool {
12953
if !s.Timestamp.Equal(o.Timestamp) {
13054
return false
13155
}
132-
56+
if s.Histogram != nil {
57+
return s.Histogram.Equal(o.Histogram)
58+
}
13359
return s.Value.Equal(o.Value)
13460
}
13561

13662
func (s Sample) String() string {
63+
if s.Histogram != nil {
64+
return fmt.Sprintf("%s => %s", s.Metric, SampleHistogramPair{
65+
Timestamp: s.Timestamp,
66+
Histogram: *s.Histogram,
67+
})
68+
}
13769
return fmt.Sprintf("%s => %s", s.Metric, SamplePair{
13870
Timestamp: s.Timestamp,
13971
Value: s.Value,
@@ -142,6 +74,19 @@ func (s Sample) String() string {
14274

14375
// MarshalJSON implements json.Marshaler.
14476
func (s Sample) MarshalJSON() ([]byte, error) {
77+
if s.Histogram != nil {
78+
v := struct {
79+
Metric Metric `json:"metric"`
80+
Histogram SampleHistogramPair `json:"histogram"`
81+
}{
82+
Metric: s.Metric,
83+
Histogram: SampleHistogramPair{
84+
Timestamp: s.Timestamp,
85+
Histogram: *s.Histogram,
86+
},
87+
}
88+
return json.Marshal(&v)
89+
}
14590
v := struct {
14691
Metric Metric `json:"metric"`
14792
Value SamplePair `json:"value"`
@@ -152,30 +97,57 @@ func (s Sample) MarshalJSON() ([]byte, error) {
15297
Value: s.Value,
15398
},
15499
}
155-
156100
return json.Marshal(&v)
157101
}
158102

103+
type sampleHistogramPairPtr struct {
104+
Timestamp Time
105+
Histogram *SampleHistogram
106+
}
107+
108+
func (s *sampleHistogramPairPtr) UnmarshalJSON(buf []byte) error {
109+
tmp := []interface{}{&s.Timestamp, &s.Histogram}
110+
wantLen := len(tmp)
111+
if err := json.Unmarshal(buf, &tmp); err != nil {
112+
return err
113+
}
114+
if gotLen := len(tmp); gotLen != wantLen {
115+
return fmt.Errorf("wrong number of fields: %d != %d", gotLen, wantLen)
116+
}
117+
return nil
118+
}
119+
159120
// UnmarshalJSON implements json.Unmarshaler.
121+
// TODO: simplify and remove the need for both sampleHistogramPairPtr and SampleHistogramPair
160122
func (s *Sample) UnmarshalJSON(b []byte) error {
161123
v := struct {
162-
Metric Metric `json:"metric"`
163-
Value SamplePair `json:"value"`
124+
Metric Metric `json:"metric"`
125+
Value SamplePair `json:"value"`
126+
Histogram sampleHistogramPairPtr `json:"histogram"`
164127
}{
165128
Metric: s.Metric,
166129
Value: SamplePair{
167130
Timestamp: s.Timestamp,
168131
Value: s.Value,
169132
},
133+
Histogram: sampleHistogramPairPtr{
134+
Timestamp: s.Timestamp,
135+
Histogram: s.Histogram,
136+
},
170137
}
171138

172139
if err := json.Unmarshal(b, &v); err != nil {
173140
return err
174141
}
175142

176143
s.Metric = v.Metric
177-
s.Timestamp = v.Value.Timestamp
178-
s.Value = v.Value.Value
144+
if v.Histogram.Histogram != nil {
145+
s.Timestamp = v.Histogram.Timestamp
146+
s.Histogram = v.Histogram.Histogram
147+
} else {
148+
s.Timestamp = v.Value.Timestamp
149+
s.Value = v.Value.Value
150+
}
179151

180152
return nil
181153
}
@@ -221,80 +193,76 @@ func (s Samples) Equal(o Samples) bool {
221193

222194
// SampleStream is a stream of Values belonging to an attached COWMetric.
223195
type SampleStream struct {
224-
Metric Metric `json:"metric"`
225-
Values []SamplePair `json:"values"`
196+
Metric Metric `json:"metric"`
197+
Values []SamplePair `json:"values"`
198+
Histograms []SampleHistogramPair `json:"histograms"`
226199
}
227200

228201
func (ss SampleStream) String() string {
229-
vals := make([]string, len(ss.Values))
202+
valuesLength := len(ss.Values)
203+
vals := make([]string, valuesLength+len(ss.Histograms))
230204
for i, v := range ss.Values {
231205
vals[i] = v.String()
232206
}
207+
for i, v := range ss.Histograms {
208+
vals[i+valuesLength] = v.String()
209+
}
233210
return fmt.Sprintf("%s =>\n%s", ss.Metric, strings.Join(vals, "\n"))
234211
}
235212

236-
// Value is a generic interface for values resulting from a query evaluation.
237-
type Value interface {
238-
Type() ValueType
239-
String() string
213+
func (ss SampleStream) MarshalJSON() ([]byte, error) {
214+
if len(ss.Histograms) > 0 && len(ss.Values) > 0 {
215+
v := struct {
216+
Metric Metric `json:"metric"`
217+
Values []SamplePair `json:"values"`
218+
Histograms []SampleHistogramPair `json:"histograms"`
219+
}{
220+
Metric: ss.Metric,
221+
Values: ss.Values,
222+
Histograms: ss.Histograms,
223+
}
224+
return json.Marshal(&v)
225+
} else if len(ss.Histograms) > 0 {
226+
v := struct {
227+
Metric Metric `json:"metric"`
228+
Histograms []SampleHistogramPair `json:"histograms"`
229+
}{
230+
Metric: ss.Metric,
231+
Histograms: ss.Histograms,
232+
}
233+
return json.Marshal(&v)
234+
} else {
235+
v := struct {
236+
Metric Metric `json:"metric"`
237+
Values []SamplePair `json:"values"`
238+
}{
239+
Metric: ss.Metric,
240+
Values: ss.Values,
241+
}
242+
return json.Marshal(&v)
243+
}
240244
}
241245

242-
func (Matrix) Type() ValueType { return ValMatrix }
243-
func (Vector) Type() ValueType { return ValVector }
244-
func (*Scalar) Type() ValueType { return ValScalar }
245-
func (*String) Type() ValueType { return ValString }
246-
247-
type ValueType int
248-
249-
const (
250-
ValNone ValueType = iota
251-
ValScalar
252-
ValVector
253-
ValMatrix
254-
ValString
255-
)
256-
257-
// MarshalJSON implements json.Marshaler.
258-
func (et ValueType) MarshalJSON() ([]byte, error) {
259-
return json.Marshal(et.String())
260-
}
246+
func (ss *SampleStream) UnmarshalJSON(b []byte) error {
247+
v := struct {
248+
Metric Metric `json:"metric"`
249+
Values []SamplePair `json:"values"`
250+
Histograms []SampleHistogramPair `json:"histograms"`
251+
}{
252+
Metric: ss.Metric,
253+
Values: ss.Values,
254+
Histograms: ss.Histograms,
255+
}
261256

262-
func (et *ValueType) UnmarshalJSON(b []byte) error {
263-
var s string
264-
if err := json.Unmarshal(b, &s); err != nil {
257+
if err := json.Unmarshal(b, &v); err != nil {
265258
return err
266259
}
267-
switch s {
268-
case "<ValNone>":
269-
*et = ValNone
270-
case "scalar":
271-
*et = ValScalar
272-
case "vector":
273-
*et = ValVector
274-
case "matrix":
275-
*et = ValMatrix
276-
case "string":
277-
*et = ValString
278-
default:
279-
return fmt.Errorf("unknown value type %q", s)
280-
}
281-
return nil
282-
}
283260

284-
func (e ValueType) String() string {
285-
switch e {
286-
case ValNone:
287-
return "<ValNone>"
288-
case ValScalar:
289-
return "scalar"
290-
case ValVector:
291-
return "vector"
292-
case ValMatrix:
293-
return "matrix"
294-
case ValString:
295-
return "string"
296-
}
297-
panic("ValueType.String: unhandled value type")
261+
ss.Metric = v.Metric
262+
ss.Values = v.Values
263+
ss.Histograms = v.Histograms
264+
265+
return nil
298266
}
299267

300268
// Scalar is a scalar value evaluated at the set timestamp.

0 commit comments

Comments
 (0)