mirror of https://github.com/prometheus/prometheus
Move COWMetric into storage/metric package
parent
ad8e8f9f24
commit
e061595352
|
@ -24,23 +24,23 @@ import (
|
|||
const (
|
||||
// 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.
|
||||
ExportedLabelPrefix LabelName = "exported_"
|
||||
ExportedLabelPrefix = "exported_"
|
||||
|
||||
// MetricNameLabel is the label name indicating the metric name of a
|
||||
// timeseries.
|
||||
MetricNameLabel LabelName = "__name__"
|
||||
MetricNameLabel = "__name__"
|
||||
|
||||
// SchemeLabel is the name of the label that holds the scheme on which to
|
||||
// scrape a target.
|
||||
SchemeLabel LabelName = "__scheme__"
|
||||
SchemeLabel = "__scheme__"
|
||||
|
||||
// AddressLabel is the name of the label that holds the address of
|
||||
// a scrape target.
|
||||
AddressLabel LabelName = "__address__"
|
||||
AddressLabel = "__address__"
|
||||
|
||||
// MetricsPathLabel is the name of the label that holds the path on which to
|
||||
// scrape a target.
|
||||
MetricsPathLabel LabelName = "__metrics_path__"
|
||||
MetricsPathLabel = "__metrics_path__"
|
||||
|
||||
// ReservedLabelPrefix is a prefix which is not legal in user-supplied
|
||||
// label names.
|
||||
|
@ -63,10 +63,10 @@ const (
|
|||
|
||||
// JobLabel is the label name indicating the job from which a timeseries
|
||||
// was scraped.
|
||||
JobLabel LabelName = "job"
|
||||
JobLabel = "job"
|
||||
|
||||
// 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
|
||||
// bucket of a histogram ("le" -> "less or equal").
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
@ -80,41 +79,3 @@ func (m Metric) Fingerprint() Fingerprint {
|
|||
func (m Metric) FastFingerprint() Fingerprint {
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -81,52 +81,3 @@ func BenchmarkMetric(b *testing.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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
func TestEmptyLabelSignature(t *testing.T) {
|
||||
func BenchmarkEmptyLabelSignature(b *testing.B) {
|
||||
input := []map[string]string{nil, {}}
|
||||
|
||||
var ms runtime.MemStats
|
||||
|
@ -262,7 +262,7 @@ func TestEmptyLabelSignature(t *testing.T) {
|
|||
runtime.ReadMemStats(&ms)
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -112,9 +112,7 @@ var dotPrecision = int(math.Log10(float64(second)))
|
|||
|
||||
// String returns a string representation of the Time.
|
||||
func (t Time) String() string {
|
||||
s := strconv.FormatInt(int64(t), 10)
|
||||
i := len(s) - dotPrecision
|
||||
return s[:i] + "." + s[i:]
|
||||
return strconv.FormatFloat(float64(t)/float64(second), 'f', -1, 64)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface.
|
||||
|
|
|
@ -67,18 +67,13 @@ func (s SamplePair) MarshalJSON() ([]byte, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal([...]interface{}{t, v})
|
||||
return []byte(fmt.Sprintf("[%s,%s]", t, v)), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler.
|
||||
func (s *SamplePair) UnmarshalJSON(b []byte) error {
|
||||
if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' {
|
||||
return fmt.Errorf("sample pair must be array")
|
||||
}
|
||||
|
||||
b = b[1 : len(b)-1]
|
||||
|
||||
return json.Unmarshal(b, [...]json.Unmarshaler{&s.Timestamp, &s.Value})
|
||||
v := [...]json.Unmarshaler{&s.Timestamp, &s.Value}
|
||||
return json.Unmarshal(b, &v)
|
||||
}
|
||||
|
||||
// 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))
|
||||
}
|
||||
|
||||
func (s *SamplePair) String() string {
|
||||
func (s SamplePair) String() string {
|
||||
return fmt.Sprintf("%s @[%s]", s.Value, s.Timestamp)
|
||||
}
|
||||
|
||||
// Sample is a sample pair associated with a metric.
|
||||
type Sample struct {
|
||||
Metric Metric
|
||||
Value SampleValue
|
||||
Timestamp Time
|
||||
Metric Metric `json:"metric"`
|
||||
Value SampleValue `json:"value"`
|
||||
Timestamp Time `json:"timestamp"`
|
||||
}
|
||||
|
||||
// Equal compares first the metrics, then the timestamp, then the value.
|
||||
|
@ -117,13 +112,53 @@ func (s *Sample) Equal(o *Sample) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func (s *Sample) String() string {
|
||||
func (s Sample) String() string {
|
||||
return fmt.Sprintf("%s => %s", s.Metric, SamplePair{
|
||||
Timestamp: s.Timestamp,
|
||||
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.
|
||||
type Samples []*Sample
|
||||
|
||||
|
@ -169,7 +204,7 @@ type SampleStream struct {
|
|||
Values []SamplePair `json:"values"`
|
||||
}
|
||||
|
||||
func (ss *SampleStream) String() string {
|
||||
func (ss SampleStream) String() string {
|
||||
vals := make([]string, len(ss.Values))
|
||||
for i, v := range ss.Values {
|
||||
vals[i] = v.String()
|
||||
|
@ -247,10 +282,33 @@ type Scalar struct {
|
|||
Timestamp Time
|
||||
}
|
||||
|
||||
func (s *Scalar) String() string {
|
||||
func (s Scalar) String() string {
|
||||
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.
|
||||
type String struct {
|
||||
Value string
|
||||
|
@ -261,6 +319,17 @@ func (s *String) String() string {
|
|||
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
|
||||
// contract is that in a Vector, all Samples have the same timestamp.
|
||||
type Vector []*Sample
|
||||
|
|
|
@ -14,10 +14,258 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math"
|
||||
"reflect"
|
||||
"sort"
|
||||
"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) {
|
||||
input := Vector{
|
||||
&Sample{
|
||||
|
|
|
@ -40,7 +40,7 @@ type Storage interface {
|
|||
// MetricsForLabelMatchers returns the metrics from storage that satisfy the given
|
||||
// label matchers. At least one label matcher must be specified that does not
|
||||
// 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
|
||||
// provided fingerprint. If the respective time series does not exist or
|
||||
// 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.
|
||||
LabelValuesForLabelName(model.LabelName) model.LabelValues
|
||||
// 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.
|
||||
// The iterator will never return samples older than retention time,
|
||||
// relative to the time NewIterator was called.
|
||||
|
|
|
@ -410,7 +410,7 @@ func (s *memorySeriesStorage) fingerprintsForLabelPairs(pairs ...model.LabelPair
|
|||
}
|
||||
|
||||
// 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 (
|
||||
equals []model.LabelPair
|
||||
filters []*metric.LabelMatcher
|
||||
|
@ -462,7 +462,7 @@ func (s *memorySeriesStorage) MetricsForLabelMatchers(matchers ...*metric.LabelM
|
|||
filters = remaining
|
||||
}
|
||||
|
||||
result := make(map[model.Fingerprint]model.COWMetric, len(resFPs))
|
||||
result := make(map[model.Fingerprint]metric.Metric, len(resFPs))
|
||||
for fp := range resFPs {
|
||||
result[fp] = s.MetricForFingerprint(fp)
|
||||
}
|
||||
|
@ -486,7 +486,7 @@ func (s *memorySeriesStorage) LabelValuesForLabelName(labelName model.LabelName)
|
|||
}
|
||||
|
||||
// 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)
|
||||
defer s.fpLocker.Unlock(fp)
|
||||
|
||||
|
@ -494,16 +494,18 @@ func (s *memorySeriesStorage) MetricForFingerprint(fp model.Fingerprint) model.C
|
|||
if ok {
|
||||
// Wrap the returned metric in a copy-on-write (COW) metric here because
|
||||
// the caller might mutate it.
|
||||
return model.COWMetric{
|
||||
return metric.Metric{
|
||||
Metric: series.metric,
|
||||
}
|
||||
}
|
||||
metric, err := s.persistence.archivedMetric(fp)
|
||||
met, err := s.persistence.archivedMetric(fp)
|
||||
if err != nil {
|
||||
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) {
|
||||
s, closer := NewTestStorage(b, 1)
|
||||
|
@ -359,7 +359,7 @@ func BenchmarkLabelMatching(b *testing.B) {
|
|||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
benchLabelMatchingRes = map[model.Fingerprint]model.COWMetric{}
|
||||
benchLabelMatchingRes = map[model.Fingerprint]metric.Metric{}
|
||||
for _, mt := range matcherTests {
|
||||
benchLabelMatchingRes = s.MetricsForLabelMatchers(mt...)
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
|
@ -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 New Issue