mirror of
https://github.com/prometheus/prometheus.git
synced 2025-03-05 20:59:13 -08:00
Move COWMetric into storage/metric package
This commit is contained in:
parent
ad8e8f9f24
commit
e061595352
14
Godeps/_workspace/src/github.com/prometheus/common/model/labels.go
generated
vendored
14
Godeps/_workspace/src/github.com/prometheus/common/model/labels.go
generated
vendored
|
@ -24,23 +24,23 @@ import (
|
||||||
const (
|
const (
|
||||||
// ExportedLabelPrefix is the prefix to prepend to the label names present in
|
// ExportedLabelPrefix is the prefix to prepend to the label names present in
|
||||||
// exported metrics if a label of the same name is added by the server.
|
// exported metrics if a label of the same name is added by the server.
|
||||||
ExportedLabelPrefix LabelName = "exported_"
|
ExportedLabelPrefix = "exported_"
|
||||||
|
|
||||||
// MetricNameLabel is the label name indicating the metric name of a
|
// MetricNameLabel is the label name indicating the metric name of a
|
||||||
// timeseries.
|
// timeseries.
|
||||||
MetricNameLabel LabelName = "__name__"
|
MetricNameLabel = "__name__"
|
||||||
|
|
||||||
// SchemeLabel is the name of the label that holds the scheme on which to
|
// SchemeLabel is the name of the label that holds the scheme on which to
|
||||||
// scrape a target.
|
// scrape a target.
|
||||||
SchemeLabel LabelName = "__scheme__"
|
SchemeLabel = "__scheme__"
|
||||||
|
|
||||||
// AddressLabel is the name of the label that holds the address of
|
// AddressLabel is the name of the label that holds the address of
|
||||||
// a scrape target.
|
// a scrape target.
|
||||||
AddressLabel LabelName = "__address__"
|
AddressLabel = "__address__"
|
||||||
|
|
||||||
// MetricsPathLabel is the name of the label that holds the path on which to
|
// MetricsPathLabel is the name of the label that holds the path on which to
|
||||||
// scrape a target.
|
// scrape a target.
|
||||||
MetricsPathLabel LabelName = "__metrics_path__"
|
MetricsPathLabel = "__metrics_path__"
|
||||||
|
|
||||||
// ReservedLabelPrefix is a prefix which is not legal in user-supplied
|
// ReservedLabelPrefix is a prefix which is not legal in user-supplied
|
||||||
// label names.
|
// label names.
|
||||||
|
@ -63,10 +63,10 @@ const (
|
||||||
|
|
||||||
// JobLabel is the label name indicating the job from which a timeseries
|
// JobLabel is the label name indicating the job from which a timeseries
|
||||||
// was scraped.
|
// was scraped.
|
||||||
JobLabel LabelName = "job"
|
JobLabel = "job"
|
||||||
|
|
||||||
// InstanceLabel is the label name used for the instance label.
|
// InstanceLabel is the label name used for the instance label.
|
||||||
InstanceLabel LabelName = "instance"
|
InstanceLabel = "instance"
|
||||||
|
|
||||||
// BucketLabel is used for the label that defines the upper bound of a
|
// BucketLabel is used for the label that defines the upper bound of a
|
||||||
// bucket of a histogram ("le" -> "less or equal").
|
// bucket of a histogram ("le" -> "less or equal").
|
||||||
|
|
39
Godeps/_workspace/src/github.com/prometheus/common/model/metric.go
generated
vendored
39
Godeps/_workspace/src/github.com/prometheus/common/model/metric.go
generated
vendored
|
@ -14,7 +14,6 @@
|
||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -80,41 +79,3 @@ func (m Metric) Fingerprint() Fingerprint {
|
||||||
func (m Metric) FastFingerprint() Fingerprint {
|
func (m Metric) FastFingerprint() Fingerprint {
|
||||||
return LabelSet(m).FastFingerprint()
|
return LabelSet(m).FastFingerprint()
|
||||||
}
|
}
|
||||||
|
|
||||||
// COWMetric wraps a Metric to enable copy-on-write access patterns.
|
|
||||||
type COWMetric struct {
|
|
||||||
Copied bool
|
|
||||||
Metric Metric
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set sets a label name in the wrapped Metric to a given value and copies the
|
|
||||||
// Metric initially, if it is not already a copy.
|
|
||||||
func (m *COWMetric) Set(ln LabelName, lv LabelValue) {
|
|
||||||
m.doCOW()
|
|
||||||
m.Metric[ln] = lv
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete deletes a given label name from the wrapped Metric and copies the
|
|
||||||
// Metric initially, if it is not already a copy.
|
|
||||||
func (m *COWMetric) Del(ln LabelName) {
|
|
||||||
m.doCOW()
|
|
||||||
delete(m.Metric, ln)
|
|
||||||
}
|
|
||||||
|
|
||||||
// doCOW copies the underlying Metric if it is not already a copy.
|
|
||||||
func (m *COWMetric) doCOW() {
|
|
||||||
if !m.Copied {
|
|
||||||
m.Metric = m.Metric.Clone()
|
|
||||||
m.Copied = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// String implements fmt.Stringer.
|
|
||||||
func (m COWMetric) String() string {
|
|
||||||
return m.Metric.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON implements json.Marshaler.
|
|
||||||
func (m COWMetric) MarshalJSON() ([]byte, error) {
|
|
||||||
return json.Marshal(m.Metric)
|
|
||||||
}
|
|
||||||
|
|
49
Godeps/_workspace/src/github.com/prometheus/common/model/metric_test.go
generated
vendored
49
Godeps/_workspace/src/github.com/prometheus/common/model/metric_test.go
generated
vendored
|
@ -81,52 +81,3 @@ func BenchmarkMetric(b *testing.B) {
|
||||||
testMetric(b)
|
testMetric(b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCOWMetric(t *testing.T) {
|
|
||||||
testMetric := Metric{
|
|
||||||
"to_delete": "test1",
|
|
||||||
"to_change": "test2",
|
|
||||||
}
|
|
||||||
|
|
||||||
scenarios := []struct {
|
|
||||||
fn func(*COWMetric)
|
|
||||||
out Metric
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
fn: func(cm *COWMetric) {
|
|
||||||
cm.Del("to_delete")
|
|
||||||
},
|
|
||||||
out: Metric{
|
|
||||||
"to_change": "test2",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fn: func(cm *COWMetric) {
|
|
||||||
cm.Set("to_change", "changed")
|
|
||||||
},
|
|
||||||
out: Metric{
|
|
||||||
"to_delete": "test1",
|
|
||||||
"to_change": "changed",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, s := range scenarios {
|
|
||||||
orig := testMetric.Clone()
|
|
||||||
cm := &COWMetric{
|
|
||||||
Metric: orig,
|
|
||||||
}
|
|
||||||
|
|
||||||
s.fn(cm)
|
|
||||||
|
|
||||||
// Test that the original metric was not modified.
|
|
||||||
if !orig.Equal(testMetric) {
|
|
||||||
t.Fatalf("%d. original metric changed; expected %v, got %v", i, testMetric, orig)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that the new metric has the right changes.
|
|
||||||
if !cm.Metric.Equal(s.out) {
|
|
||||||
t.Fatalf("%d. copied metric doesn't contain expected changes; expected %v, got %v", i, s.out, cm.Metric)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
4
Godeps/_workspace/src/github.com/prometheus/common/model/signature_test.go
generated
vendored
4
Godeps/_workspace/src/github.com/prometheus/common/model/signature_test.go
generated
vendored
|
@ -247,7 +247,7 @@ func BenchmarkMetricToFastFingerprintTriple(b *testing.B) {
|
||||||
benchmarkMetricToFastFingerprint(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676)
|
benchmarkMetricToFastFingerprint(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEmptyLabelSignature(t *testing.T) {
|
func BenchmarkEmptyLabelSignature(b *testing.B) {
|
||||||
input := []map[string]string{nil, {}}
|
input := []map[string]string{nil, {}}
|
||||||
|
|
||||||
var ms runtime.MemStats
|
var ms runtime.MemStats
|
||||||
|
@ -262,7 +262,7 @@ func TestEmptyLabelSignature(t *testing.T) {
|
||||||
runtime.ReadMemStats(&ms)
|
runtime.ReadMemStats(&ms)
|
||||||
|
|
||||||
if got := ms.Alloc; alloc != got {
|
if got := ms.Alloc; alloc != got {
|
||||||
t.Fatal("expected LabelsToSignature with empty labels not to perform allocations")
|
b.Fatal("expected LabelsToSignature with empty labels not to perform allocations")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
4
Godeps/_workspace/src/github.com/prometheus/common/model/time.go
generated
vendored
4
Godeps/_workspace/src/github.com/prometheus/common/model/time.go
generated
vendored
|
@ -112,9 +112,7 @@ var dotPrecision = int(math.Log10(float64(second)))
|
||||||
|
|
||||||
// String returns a string representation of the Time.
|
// String returns a string representation of the Time.
|
||||||
func (t Time) String() string {
|
func (t Time) String() string {
|
||||||
s := strconv.FormatInt(int64(t), 10)
|
return strconv.FormatFloat(float64(t)/float64(second), 'f', -1, 64)
|
||||||
i := len(s) - dotPrecision
|
|
||||||
return s[:i] + "." + s[i:]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalJSON implements the json.Marshaler interface.
|
// MarshalJSON implements the json.Marshaler interface.
|
||||||
|
|
99
Godeps/_workspace/src/github.com/prometheus/common/model/value.go
generated
vendored
99
Godeps/_workspace/src/github.com/prometheus/common/model/value.go
generated
vendored
|
@ -67,18 +67,13 @@ func (s SamplePair) MarshalJSON() ([]byte, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return json.Marshal([...]interface{}{t, v})
|
return []byte(fmt.Sprintf("[%s,%s]", t, v)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalJSON implements json.Unmarshaler.
|
// UnmarshalJSON implements json.Unmarshaler.
|
||||||
func (s *SamplePair) UnmarshalJSON(b []byte) error {
|
func (s *SamplePair) UnmarshalJSON(b []byte) error {
|
||||||
if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' {
|
v := [...]json.Unmarshaler{&s.Timestamp, &s.Value}
|
||||||
return fmt.Errorf("sample pair must be array")
|
return json.Unmarshal(b, &v)
|
||||||
}
|
|
||||||
|
|
||||||
b = b[1 : len(b)-1]
|
|
||||||
|
|
||||||
return json.Unmarshal(b, [...]json.Unmarshaler{&s.Timestamp, &s.Value})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equal returns true if this SamplePair and o have equal Values and equal
|
// Equal returns true if this SamplePair and o have equal Values and equal
|
||||||
|
@ -87,15 +82,15 @@ func (s *SamplePair) Equal(o *SamplePair) bool {
|
||||||
return s == o || (s.Value == o.Value && s.Timestamp.Equal(o.Timestamp))
|
return s == o || (s.Value == o.Value && s.Timestamp.Equal(o.Timestamp))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SamplePair) String() string {
|
func (s SamplePair) String() string {
|
||||||
return fmt.Sprintf("%s @[%s]", s.Value, s.Timestamp)
|
return fmt.Sprintf("%s @[%s]", s.Value, s.Timestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sample is a sample pair associated with a metric.
|
// Sample is a sample pair associated with a metric.
|
||||||
type Sample struct {
|
type Sample struct {
|
||||||
Metric Metric
|
Metric Metric `json:"metric"`
|
||||||
Value SampleValue
|
Value SampleValue `json:"value"`
|
||||||
Timestamp Time
|
Timestamp Time `json:"timestamp"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equal compares first the metrics, then the timestamp, then the value.
|
// Equal compares first the metrics, then the timestamp, then the value.
|
||||||
|
@ -117,13 +112,53 @@ func (s *Sample) Equal(o *Sample) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Sample) String() string {
|
func (s Sample) String() string {
|
||||||
return fmt.Sprintf("%s => %s", s.Metric, SamplePair{
|
return fmt.Sprintf("%s => %s", s.Metric, SamplePair{
|
||||||
Timestamp: s.Timestamp,
|
Timestamp: s.Timestamp,
|
||||||
Value: s.Value,
|
Value: s.Value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements json.Marshaler.
|
||||||
|
func (s Sample) MarshalJSON() ([]byte, error) {
|
||||||
|
v := struct {
|
||||||
|
Metric Metric `json:"metric"`
|
||||||
|
Value SamplePair `json:"value"`
|
||||||
|
}{
|
||||||
|
Metric: s.Metric,
|
||||||
|
Value: SamplePair{
|
||||||
|
Timestamp: s.Timestamp,
|
||||||
|
Value: s.Value,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(&v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements json.Unmarshaler.
|
||||||
|
func (s *Sample) UnmarshalJSON(b []byte) error {
|
||||||
|
v := struct {
|
||||||
|
Metric Metric `json:"metric"`
|
||||||
|
Value SamplePair `json:"value"`
|
||||||
|
}{
|
||||||
|
Metric: s.Metric,
|
||||||
|
Value: SamplePair{
|
||||||
|
Timestamp: s.Timestamp,
|
||||||
|
Value: s.Value,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Metric = v.Metric
|
||||||
|
s.Timestamp = v.Value.Timestamp
|
||||||
|
s.Value = v.Value.Value
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Samples is a sortable Sample slice. It implements sort.Interface.
|
// Samples is a sortable Sample slice. It implements sort.Interface.
|
||||||
type Samples []*Sample
|
type Samples []*Sample
|
||||||
|
|
||||||
|
@ -169,7 +204,7 @@ type SampleStream struct {
|
||||||
Values []SamplePair `json:"values"`
|
Values []SamplePair `json:"values"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ss *SampleStream) String() string {
|
func (ss SampleStream) String() string {
|
||||||
vals := make([]string, len(ss.Values))
|
vals := make([]string, len(ss.Values))
|
||||||
for i, v := range ss.Values {
|
for i, v := range ss.Values {
|
||||||
vals[i] = v.String()
|
vals[i] = v.String()
|
||||||
|
@ -247,10 +282,33 @@ type Scalar struct {
|
||||||
Timestamp Time
|
Timestamp Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Scalar) String() string {
|
func (s Scalar) String() string {
|
||||||
return fmt.Sprintf("scalar: %v @[%v]", s.Value, s.Timestamp)
|
return fmt.Sprintf("scalar: %v @[%v]", s.Value, s.Timestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements json.Marshaler.
|
||||||
|
func (s Scalar) MarshalJSON() ([]byte, error) {
|
||||||
|
v := strconv.FormatFloat(float64(s.Value), 'f', -1, 64)
|
||||||
|
return json.Marshal([...]interface{}{s.Timestamp, string(v)})
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements json.Unmarshaler.
|
||||||
|
func (s *Scalar) UnmarshalJSON(b []byte) error {
|
||||||
|
var f string
|
||||||
|
v := [...]interface{}{&s.Timestamp, &f}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
value, err := strconv.ParseFloat(f, 64)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error parsing sample value: %s", err)
|
||||||
|
}
|
||||||
|
s.Value = SampleValue(value)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// String is a string value evaluated at the set timestamp.
|
// String is a string value evaluated at the set timestamp.
|
||||||
type String struct {
|
type String struct {
|
||||||
Value string
|
Value string
|
||||||
|
@ -261,6 +319,17 @@ func (s *String) String() string {
|
||||||
return s.Value
|
return s.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements json.Marshaler.
|
||||||
|
func (s String) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal([]interface{}{s.Timestamp, s.Value})
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements json.Unmarshaler.
|
||||||
|
func (s *String) UnmarshalJSON(b []byte) error {
|
||||||
|
v := [...]interface{}{&s.Timestamp, &s.Value}
|
||||||
|
return json.Unmarshal(b, &v)
|
||||||
|
}
|
||||||
|
|
||||||
// Vector is basically only an alias for Samples, but the
|
// Vector is basically only an alias for Samples, but the
|
||||||
// contract is that in a Vector, all Samples have the same timestamp.
|
// contract is that in a Vector, all Samples have the same timestamp.
|
||||||
type Vector []*Sample
|
type Vector []*Sample
|
||||||
|
|
248
Godeps/_workspace/src/github.com/prometheus/common/model/value_test.go
generated
vendored
248
Godeps/_workspace/src/github.com/prometheus/common/model/value_test.go
generated
vendored
|
@ -14,10 +14,258 @@
|
||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"math"
|
||||||
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestSamplePairJSON(t *testing.T) {
|
||||||
|
input := []struct {
|
||||||
|
plain string
|
||||||
|
value SamplePair
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
plain: `[1234.567,"123.1"]`,
|
||||||
|
value: SamplePair{
|
||||||
|
Value: 123.1,
|
||||||
|
Timestamp: 1234567,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range input {
|
||||||
|
b, err := json.Marshal(test.value)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(b) != test.plain {
|
||||||
|
t.Errorf("encoding error: expected %q, got %q", test.plain, b)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var sp SamplePair
|
||||||
|
err = json.Unmarshal(b, &sp)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if sp != test.value {
|
||||||
|
t.Errorf("decoding error: expected %v, got %v", test.value, sp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSampleJSON(t *testing.T) {
|
||||||
|
input := []struct {
|
||||||
|
plain string
|
||||||
|
value Sample
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
plain: `{"metric":{"__name__":"test_metric"},"value":[1234.567,"123.1"]}`,
|
||||||
|
value: Sample{
|
||||||
|
Metric: Metric{
|
||||||
|
MetricNameLabel: "test_metric",
|
||||||
|
},
|
||||||
|
Value: 123.1,
|
||||||
|
Timestamp: 1234567,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range input {
|
||||||
|
b, err := json.Marshal(test.value)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(b) != test.plain {
|
||||||
|
t.Errorf("encoding error: expected %q, got %q", test.plain, b)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var sv Sample
|
||||||
|
err = json.Unmarshal(b, &sv)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(sv, test.value) {
|
||||||
|
t.Errorf("decoding error: expected %v, got %v", test.value, sv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVectorJSON(t *testing.T) {
|
||||||
|
input := []struct {
|
||||||
|
plain string
|
||||||
|
value Vector
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
plain: `[]`,
|
||||||
|
value: Vector{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
plain: `[{"metric":{"__name__":"test_metric"},"value":[1234.567,"123.1"]}]`,
|
||||||
|
value: Vector{&Sample{
|
||||||
|
Metric: Metric{
|
||||||
|
MetricNameLabel: "test_metric",
|
||||||
|
},
|
||||||
|
Value: 123.1,
|
||||||
|
Timestamp: 1234567,
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
plain: `[{"metric":{"__name__":"test_metric"},"value":[1234.567,"123.1"]},{"metric":{"foo":"bar"},"value":[1.234,"+Inf"]}]`,
|
||||||
|
value: Vector{
|
||||||
|
&Sample{
|
||||||
|
Metric: Metric{
|
||||||
|
MetricNameLabel: "test_metric",
|
||||||
|
},
|
||||||
|
Value: 123.1,
|
||||||
|
Timestamp: 1234567,
|
||||||
|
},
|
||||||
|
&Sample{
|
||||||
|
Metric: Metric{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
Value: SampleValue(math.Inf(1)),
|
||||||
|
Timestamp: 1234,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range input {
|
||||||
|
b, err := json.Marshal(test.value)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(b) != test.plain {
|
||||||
|
t.Errorf("encoding error: expected %q, got %q", test.plain, b)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var vec Vector
|
||||||
|
err = json.Unmarshal(b, &vec)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(vec, test.value) {
|
||||||
|
t.Errorf("decoding error: expected %v, got %v", test.value, vec)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestScalarJSON(t *testing.T) {
|
||||||
|
input := []struct {
|
||||||
|
plain string
|
||||||
|
value Scalar
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
plain: `[123.456,"456"]`,
|
||||||
|
value: Scalar{
|
||||||
|
Timestamp: 123456,
|
||||||
|
Value: 456,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
plain: `[123123.456,"+Inf"]`,
|
||||||
|
value: Scalar{
|
||||||
|
Timestamp: 123123456,
|
||||||
|
Value: SampleValue(math.Inf(1)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
plain: `[123123.456,"-Inf"]`,
|
||||||
|
value: Scalar{
|
||||||
|
Timestamp: 123123456,
|
||||||
|
Value: SampleValue(math.Inf(-1)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range input {
|
||||||
|
b, err := json.Marshal(test.value)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(b) != test.plain {
|
||||||
|
t.Errorf("encoding error: expected %q, got %q", test.plain, b)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var sv Scalar
|
||||||
|
err = json.Unmarshal(b, &sv)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if sv != test.value {
|
||||||
|
t.Errorf("decoding error: expected %v, got %v", test.value, sv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStringJSON(t *testing.T) {
|
||||||
|
input := []struct {
|
||||||
|
plain string
|
||||||
|
value String
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
plain: `[123.456,"test"]`,
|
||||||
|
value: String{
|
||||||
|
Timestamp: 123456,
|
||||||
|
Value: "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
plain: `[123123.456,"台北"]`,
|
||||||
|
value: String{
|
||||||
|
Timestamp: 123123456,
|
||||||
|
Value: "台北",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range input {
|
||||||
|
b, err := json.Marshal(test.value)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(b) != test.plain {
|
||||||
|
t.Errorf("encoding error: expected %q, got %q", test.plain, b)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var sv String
|
||||||
|
err = json.Unmarshal(b, &sv)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if sv != test.value {
|
||||||
|
t.Errorf("decoding error: expected %v, got %v", test.value, sv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestVectorSort(t *testing.T) {
|
func TestVectorSort(t *testing.T) {
|
||||||
input := Vector{
|
input := Vector{
|
||||||
&Sample{
|
&Sample{
|
||||||
|
|
|
@ -40,7 +40,7 @@ type Storage interface {
|
||||||
// MetricsForLabelMatchers returns the metrics from storage that satisfy the given
|
// MetricsForLabelMatchers returns the metrics from storage that satisfy the given
|
||||||
// label matchers. At least one label matcher must be specified that does not
|
// label matchers. At least one label matcher must be specified that does not
|
||||||
// match the empty string.
|
// match the empty string.
|
||||||
MetricsForLabelMatchers(...*metric.LabelMatcher) map[model.Fingerprint]model.COWMetric
|
MetricsForLabelMatchers(...*metric.LabelMatcher) map[model.Fingerprint]metric.Metric
|
||||||
// LastSamplePairForFingerprint returns the last sample pair for the
|
// LastSamplePairForFingerprint returns the last sample pair for the
|
||||||
// provided fingerprint. If the respective time series does not exist or
|
// provided fingerprint. If the respective time series does not exist or
|
||||||
// has an evicted head chunk, nil is returned.
|
// has an evicted head chunk, nil is returned.
|
||||||
|
@ -48,7 +48,7 @@ type Storage interface {
|
||||||
// Get all of the label values that are associated with a given label name.
|
// Get all of the label values that are associated with a given label name.
|
||||||
LabelValuesForLabelName(model.LabelName) model.LabelValues
|
LabelValuesForLabelName(model.LabelName) model.LabelValues
|
||||||
// Get the metric associated with the provided fingerprint.
|
// Get the metric associated with the provided fingerprint.
|
||||||
MetricForFingerprint(model.Fingerprint) model.COWMetric
|
MetricForFingerprint(model.Fingerprint) metric.Metric
|
||||||
// Construct an iterator for a given fingerprint.
|
// Construct an iterator for a given fingerprint.
|
||||||
// The iterator will never return samples older than retention time,
|
// The iterator will never return samples older than retention time,
|
||||||
// relative to the time NewIterator was called.
|
// relative to the time NewIterator was called.
|
||||||
|
|
|
@ -410,7 +410,7 @@ func (s *memorySeriesStorage) fingerprintsForLabelPairs(pairs ...model.LabelPair
|
||||||
}
|
}
|
||||||
|
|
||||||
// MetricsForLabelMatchers implements Storage.
|
// MetricsForLabelMatchers implements Storage.
|
||||||
func (s *memorySeriesStorage) MetricsForLabelMatchers(matchers ...*metric.LabelMatcher) map[model.Fingerprint]model.COWMetric {
|
func (s *memorySeriesStorage) MetricsForLabelMatchers(matchers ...*metric.LabelMatcher) map[model.Fingerprint]metric.Metric {
|
||||||
var (
|
var (
|
||||||
equals []model.LabelPair
|
equals []model.LabelPair
|
||||||
filters []*metric.LabelMatcher
|
filters []*metric.LabelMatcher
|
||||||
|
@ -462,7 +462,7 @@ func (s *memorySeriesStorage) MetricsForLabelMatchers(matchers ...*metric.LabelM
|
||||||
filters = remaining
|
filters = remaining
|
||||||
}
|
}
|
||||||
|
|
||||||
result := make(map[model.Fingerprint]model.COWMetric, len(resFPs))
|
result := make(map[model.Fingerprint]metric.Metric, len(resFPs))
|
||||||
for fp := range resFPs {
|
for fp := range resFPs {
|
||||||
result[fp] = s.MetricForFingerprint(fp)
|
result[fp] = s.MetricForFingerprint(fp)
|
||||||
}
|
}
|
||||||
|
@ -486,7 +486,7 @@ func (s *memorySeriesStorage) LabelValuesForLabelName(labelName model.LabelName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MetricForFingerprint implements Storage.
|
// MetricForFingerprint implements Storage.
|
||||||
func (s *memorySeriesStorage) MetricForFingerprint(fp model.Fingerprint) model.COWMetric {
|
func (s *memorySeriesStorage) MetricForFingerprint(fp model.Fingerprint) metric.Metric {
|
||||||
s.fpLocker.Lock(fp)
|
s.fpLocker.Lock(fp)
|
||||||
defer s.fpLocker.Unlock(fp)
|
defer s.fpLocker.Unlock(fp)
|
||||||
|
|
||||||
|
@ -494,16 +494,18 @@ func (s *memorySeriesStorage) MetricForFingerprint(fp model.Fingerprint) model.C
|
||||||
if ok {
|
if ok {
|
||||||
// Wrap the returned metric in a copy-on-write (COW) metric here because
|
// Wrap the returned metric in a copy-on-write (COW) metric here because
|
||||||
// the caller might mutate it.
|
// the caller might mutate it.
|
||||||
return model.COWMetric{
|
return metric.Metric{
|
||||||
Metric: series.metric,
|
Metric: series.metric,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
metric, err := s.persistence.archivedMetric(fp)
|
met, err := s.persistence.archivedMetric(fp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error retrieving archived metric for fingerprint %v: %v", fp, err)
|
log.Errorf("Error retrieving archived metric for fingerprint %v: %v", fp, err)
|
||||||
}
|
}
|
||||||
return model.COWMetric{
|
|
||||||
Metric: metric,
|
return metric.Metric{
|
||||||
|
Metric: met,
|
||||||
|
Copied: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -276,7 +276,7 @@ func TestFingerprintsForLabels(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var benchLabelMatchingRes map[model.Fingerprint]model.COWMetric
|
var benchLabelMatchingRes map[model.Fingerprint]metric.Metric
|
||||||
|
|
||||||
func BenchmarkLabelMatching(b *testing.B) {
|
func BenchmarkLabelMatching(b *testing.B) {
|
||||||
s, closer := NewTestStorage(b, 1)
|
s, closer := NewTestStorage(b, 1)
|
||||||
|
@ -359,7 +359,7 @@ func BenchmarkLabelMatching(b *testing.B) {
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
benchLabelMatchingRes = map[model.Fingerprint]model.COWMetric{}
|
benchLabelMatchingRes = map[model.Fingerprint]metric.Metric{}
|
||||||
for _, mt := range matcherTests {
|
for _, mt := range matcherTests {
|
||||||
benchLabelMatchingRes = s.MetricsForLabelMatchers(mt...)
|
benchLabelMatchingRes = s.MetricsForLabelMatchers(mt...)
|
||||||
}
|
}
|
||||||
|
|
63
storage/metric/metric.go
Normal file
63
storage/metric/metric.go
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
// Copyright 2014 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package metric
|
||||||
|
|
||||||
|
import "github.com/prometheus/common/model"
|
||||||
|
|
||||||
|
// Metric wraps a model.Metric and copies it upon modification if Copied is false.
|
||||||
|
type Metric struct {
|
||||||
|
Copied bool
|
||||||
|
Metric model.Metric
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sets a label name in the wrapped Metric to a given value and copies the
|
||||||
|
// Metric initially, if it is not already a copy.
|
||||||
|
func (m *Metric) Set(ln model.LabelName, lv model.LabelValue) {
|
||||||
|
m.Copy()
|
||||||
|
m.Metric[ln] = lv
|
||||||
|
}
|
||||||
|
|
||||||
|
// Del deletes a given label name from the wrapped Metric and copies the
|
||||||
|
// Metric initially, if it is not already a copy.
|
||||||
|
func (m *Metric) Del(ln model.LabelName) {
|
||||||
|
m.Copy()
|
||||||
|
delete(m.Metric, ln)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the value for the given label name. An empty value is returned
|
||||||
|
// if the label does not exist in the metric.
|
||||||
|
func (m *Metric) Get(ln model.LabelName) model.LabelValue {
|
||||||
|
return m.Metric[ln]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets behaves as Get but the returned boolean is false iff the label
|
||||||
|
// does not exist.
|
||||||
|
func (m *Metric) Gets(ln model.LabelName) (model.LabelValue, bool) {
|
||||||
|
lv, ok := m.Metric[ln]
|
||||||
|
return lv, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the underlying Metric if it is not already a copy.
|
||||||
|
func (m *Metric) Copy() *Metric {
|
||||||
|
if !m.Copied {
|
||||||
|
m.Metric = m.Metric.Clone()
|
||||||
|
m.Copied = true
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// String implements fmt.Stringer.
|
||||||
|
func (m Metric) String() string {
|
||||||
|
return m.Metric.String()
|
||||||
|
}
|
70
storage/metric/metric_test.go
Normal file
70
storage/metric/metric_test.go
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
// Copyright 2014 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package metric
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/prometheus/common/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMetric(t *testing.T) {
|
||||||
|
testMetric := model.Metric{
|
||||||
|
"to_delete": "test1",
|
||||||
|
"to_change": "test2",
|
||||||
|
}
|
||||||
|
|
||||||
|
scenarios := []struct {
|
||||||
|
fn func(*Metric)
|
||||||
|
out Metric
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
fn: func(cm *etric) {
|
||||||
|
cm.Del("to_delete")
|
||||||
|
},
|
||||||
|
out: Metric{
|
||||||
|
"to_change": "test2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fn: func(cm *COWMetric) {
|
||||||
|
cm.Set("to_change", "changed")
|
||||||
|
},
|
||||||
|
out: Metric{
|
||||||
|
"to_delete": "test1",
|
||||||
|
"to_change": "changed",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, s := range scenarios {
|
||||||
|
orig := testMetric.Clone()
|
||||||
|
cm := &Metric{
|
||||||
|
Metric: orig,
|
||||||
|
Copied: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
s.fn(cm)
|
||||||
|
|
||||||
|
// Test that the original metric was not modified.
|
||||||
|
if !orig.Equal(testMetric) {
|
||||||
|
t.Fatalf("%d. original metric changed; expected %v, got %v", i, testMetric, orig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that the new metric has the right changes.
|
||||||
|
if !cm.Metric.Equal(s.out) {
|
||||||
|
t.Fatalf("%d. copied metric doesn't contain expected changes; expected %v, got %v", i, s.out, cm.Metric)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue