diff --git a/pkg/labels/labels.go b/pkg/labels/labels.go new file mode 100644 index 000000000..8025feb67 --- /dev/null +++ b/pkg/labels/labels.go @@ -0,0 +1,153 @@ +package labels + +import ( + "bytes" + "sort" + "strconv" + "strings" + + "github.com/cespare/xxhash" +) + +const sep = '\xff' + +// Well-known label names used by Prometheus components. +const ( + MetricName = "__name__" + AlertName = "alertname" +) + +// Label is a key/value pair of strings. +type Label struct { + Name, Value string +} + +// Labels is a sorted set of labels. Order has to be guaranteed upon +// instantiation. +type Labels []Label + +func (ls Labels) Len() int { return len(ls) } +func (ls Labels) Swap(i, j int) { ls[i], ls[j] = ls[j], ls[i] } +func (ls Labels) Less(i, j int) bool { return ls[i].Name < ls[j].Name } + +func (ls Labels) String() string { + var b bytes.Buffer + + b.WriteByte('{') + for i, l := range ls { + if i > 0 { + b.WriteByte(',') + } + b.WriteString(l.Name) + b.WriteByte('=') + b.WriteString(strconv.Quote(l.Value)) + } + b.WriteByte('}') + + return b.String() +} + +// Hash returns a hash value for the label set. +func (ls Labels) Hash() uint64 { + b := make([]byte, 0, 1024) + + for _, v := range ls { + b = append(b, v.Name...) + b = append(b, sep) + b = append(b, v.Value...) + b = append(b, sep) + } + return xxhash.Sum64(b) +} + +// Get returns the value for the label with the given name. +// Returns an empty string if the label doesn't exist. +func (ls Labels) Get(name string) string { + for _, l := range ls { + if l.Name == name { + return l.Value + } + } + return "" +} + +// Equals returns whether the two label sets are equal. +func (ls Labels) Equals(o Labels) bool { + if len(ls) != len(o) { + return false + } + for i, l := range ls { + if l.Name != o[i].Name || l.Value != o[i].Value { + return false + } + } + return true +} + +// Map returns a string map of the labels. +func (ls Labels) Map() map[string]string { + m := make(map[string]string, len(ls)) + for _, l := range ls { + m[l.Name] = l.Value + } + return m +} + +// New returns a sorted Labels from the given labels. +// The caller has to guarantee that all label names are unique. +func New(ls ...Label) Labels { + set := make(Labels, 0, len(ls)) + for _, l := range ls { + set = append(set, l) + } + sort.Sort(set) + + return set +} + +// FromMap returns new sorted Labels from the given map. +func FromMap(m map[string]string) Labels { + l := make([]Label, 0, len(m)) + for k, v := range m { + l = append(l, Label{Name: k, Value: v}) + } + return New(l...) +} + +// FromStrings creates new labels from pairs of strings. +func FromStrings(ss ...string) Labels { + if len(ss)%2 != 0 { + panic("invalid number of strings") + } + var res Labels + for i := 0; i < len(ss); i += 2 { + res = append(res, Label{Name: ss[i], Value: ss[i+1]}) + } + + sort.Sort(res) + return res +} + +// Compare compares the two label sets. +// The result will be 0 if a==b, <0 if a < b, and >0 if a > b. +func Compare(a, b Labels) int { + l := len(a) + if len(b) < l { + l = len(b) + } + + for i := 0; i < l; i++ { + if d := strings.Compare(a[i].Name, b[i].Name); d != 0 { + return d + } + if d := strings.Compare(a[i].Value, b[i].Value); d != 0 { + return d + } + } + // If all labels so far were in common, the set with fewer labels comes first. + return len(a) - len(b) +} + +type LabelsBuilder struct { + base Labels +} diff --git a/promql/ast.go b/promql/ast.go index e24e0cb21..a765094d6 100644 --- a/promql/ast.go +++ b/promql/ast.go @@ -17,9 +17,11 @@ import ( "fmt" "regexp" "time" + "unsafe" "github.com/fabxc/tsdb" - "github.com/fabxc/tsdb/labels" + tsdbLabels "github.com/fabxc/tsdb/labels" + "github.com/prometheus/prometheus/pkg/labels" ) // Node is a generic interface for all nodes in an AST. @@ -368,31 +370,39 @@ func NewLabelMatcher(t MatchType, n, v string) (*LabelMatcher, error) { return m, nil } +func toTSDBLabels(l labels.Labels) tsdbLabels.Labels { + return *(*tsdbLabels.Labels)(unsafe.Pointer(&l)) +} + +func toLabels(l tsdbLabels.Labels) labels.Labels { + return *(*labels.Labels)(unsafe.Pointer(&l)) +} + func (m *LabelMatcher) String() string { return fmt.Sprintf("%s%s%q", m.Name, m.Type, m.Value) } -func (m *LabelMatcher) matcher() labels.Matcher { +func (m *LabelMatcher) matcher() tsdbLabels.Matcher { switch m.Type { case MatchEqual: - return labels.NewEqualMatcher(m.Name, m.Value) + return tsdbLabels.NewEqualMatcher(m.Name, m.Value) case MatchNotEqual: - return labels.Not(labels.NewEqualMatcher(m.Name, m.Value)) + return tsdbLabels.Not(tsdbLabels.NewEqualMatcher(m.Name, m.Value)) case MatchRegexp: - res, err := labels.NewRegexpMatcher(m.Name, m.Value) + res, err := tsdbLabels.NewRegexpMatcher(m.Name, m.Value) if err != nil { panic(err) } return res case MatchNotRegexp: - res, err := labels.NewRegexpMatcher(m.Name, m.Value) + res, err := tsdbLabels.NewRegexpMatcher(m.Name, m.Value) if err != nil { panic(err) } - return labels.Not(res) + return tsdbLabels.Not(res) } panic("promql.LabelMatcher.matcher: invalid matcher type") } diff --git a/promql/engine.go b/promql/engine.go index e566176b0..14bec5799 100644 --- a/promql/engine.go +++ b/promql/engine.go @@ -23,8 +23,9 @@ import ( "time" "github.com/fabxc/tsdb" - "github.com/fabxc/tsdb/labels" + tsdbLabels "github.com/fabxc/tsdb/labels" "github.com/prometheus/common/log" + "github.com/prometheus/prometheus/pkg/labels" "golang.org/x/net/context" "github.com/prometheus/prometheus/util/stats" @@ -555,7 +556,7 @@ func (ng *Engine) populateIterators(ctx context.Context, s *EvalStmt) (tsdb.Quer Inspect(s.Expr, func(node Node) bool { switch n := node.(type) { case *VectorSelector: - sel := make(labels.Selector, 0, len(n.LabelMatchers)) + sel := make(tsdbLabels.Selector, 0, len(n.LabelMatchers)) for _, m := range n.LabelMatchers { sel = append(sel, m.matcher()) } @@ -569,7 +570,7 @@ func (ng *Engine) populateIterators(ctx context.Context, s *EvalStmt) (tsdb.Quer n.iterators = append(n.iterators, it) } case *MatrixSelector: - sel := make(labels.Selector, 0, len(n.LabelMatchers)) + sel := make(tsdbLabels.Selector, 0, len(n.LabelMatchers)) for _, m := range n.LabelMatchers { sel = append(sel, m.matcher()) } @@ -805,7 +806,7 @@ func (ev *evaluator) VectorSelector(node *VectorSelector) Vector { } vec = append(vec, Sample{ - Metric: node.series[i].Labels(), + Metric: toLabels(node.series[i].Labels()), Point: Point{V: v, T: ev.Timestamp}, }) } @@ -823,7 +824,7 @@ func (ev *evaluator) MatrixSelector(node *MatrixSelector) Matrix { for i, it := range node.iterators { ss := Series{ - Metric: node.series[i].Labels(), + Metric: toLabels(node.series[i].Labels()), Points: make([]Point, 0, 16), } diff --git a/promql/functions.go b/promql/functions.go index b8167cd8a..97d4a520c 100644 --- a/promql/functions.go +++ b/promql/functions.go @@ -20,8 +20,8 @@ import ( "strconv" "time" - "github.com/fabxc/tsdb/labels" "github.com/prometheus/common/model" + "github.com/prometheus/prometheus/pkg/labels" ) // Function represents a function of the expression language and is diff --git a/promql/parse.go b/promql/parse.go index 09bb761a7..525efd2ec 100644 --- a/promql/parse.go +++ b/promql/parse.go @@ -21,9 +21,9 @@ import ( "strings" "time" - "github.com/fabxc/tsdb/labels" "github.com/prometheus/common/log" "github.com/prometheus/common/model" + "github.com/prometheus/prometheus/pkg/labels" "github.com/prometheus/prometheus/util/strutil" ) diff --git a/promql/printer_test.go b/promql/printer_test.go index 1f64e2aee..7407f717c 100644 --- a/promql/printer_test.go +++ b/promql/printer_test.go @@ -17,7 +17,7 @@ import ( "testing" "time" - "github.com/fabxc/tsdb/labels" + "github.com/prometheus/prometheus/pkg/labels" ) func TestStatementString(t *testing.T) { diff --git a/promql/quantile.go b/promql/quantile.go index 7761316ab..c7c905460 100644 --- a/promql/quantile.go +++ b/promql/quantile.go @@ -17,8 +17,8 @@ import ( "math" "sort" - "github.com/fabxc/tsdb/labels" "github.com/prometheus/common/model" + "github.com/prometheus/prometheus/pkg/labels" ) // Helpers to calculate quantiles. diff --git a/promql/test.go b/promql/test.go index ccaa6550b..fbb02e84e 100644 --- a/promql/test.go +++ b/promql/test.go @@ -22,8 +22,8 @@ import ( "strings" "time" - "github.com/fabxc/tsdb/labels" "github.com/prometheus/common/model" + "github.com/prometheus/prometheus/pkg/labels" "golang.org/x/net/context" "github.com/prometheus/prometheus/storage"