Skip to content

Commit

Permalink
Add count_values() aggregator.
Browse files Browse the repository at this point in the history
This is useful for counting how many instances
of a job are running a particular version/build.

Fixes prometheus#622
  • Loading branch information
brian-brazil committed Jul 5, 2016
1 parent 6f19e41 commit 1669073
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 12 deletions.
18 changes: 16 additions & 2 deletions promql/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -1076,6 +1076,13 @@ func (ev *evaluator) aggregation(op itemType, grouping model.LabelNames, without
return vector{}
}
}
var valueLabel model.LabelName
if op == itemCountValues {
valueLabel = model.LabelName(ev.evalString(param).Value)
if !without {
grouping = append(grouping, valueLabel)
}
}

for _, s := range vec {
withoutMetric := s.Metric
Expand All @@ -1084,6 +1091,13 @@ func (ev *evaluator) aggregation(op itemType, grouping model.LabelNames, without
withoutMetric.Del(l)
}
withoutMetric.Del(model.MetricNameLabel)
if op == itemCountValues {
withoutMetric.Set(valueLabel, model.LabelValue(s.Value.String()))
}
} else {
if op == itemCountValues {
s.Metric.Set(valueLabel, model.LabelValue(s.Value.String()))
}
}

var groupingKey uint64
Expand Down Expand Up @@ -1147,7 +1161,7 @@ func (ev *evaluator) aggregation(op itemType, grouping model.LabelNames, without
if groupedResult.value > s.Value || math.IsNaN(float64(groupedResult.value)) {
groupedResult.value = s.Value
}
case itemCount:
case itemCount, itemCountValues:
groupedResult.groupCount++
case itemStdvar, itemStddev:
groupedResult.value += s.Value
Expand Down Expand Up @@ -1179,7 +1193,7 @@ func (ev *evaluator) aggregation(op itemType, grouping model.LabelNames, without
switch op {
case itemAvg:
aggr.value = aggr.value / model.SampleValue(aggr.groupCount)
case itemCount:
case itemCount, itemCountValues:
aggr.value = model.SampleValue(aggr.groupCount)
case itemStdvar:
avg := float64(aggr.value) / float64(aggr.groupCount)
Expand Down
24 changes: 14 additions & 10 deletions promql/lex.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ func (i itemType) isAggregator() bool { return i > aggregatorsStart && i < aggre

// isAggregator returns true if the item is an aggregator that takes a parameter.
// Returns false otherwise
func (i itemType) isAggregatorWithParam() bool { return i == itemTopK || i == itemBottomK }
func (i itemType) isAggregatorWithParam() bool {
return i == itemTopK || i == itemBottomK || i == itemCountValues
}

// isKeyword returns true if the item corresponds to a keyword.
// Returns false otherwise.
Expand Down Expand Up @@ -176,6 +178,7 @@ const (
itemStdvar
itemTopK
itemBottomK
itemCountValues
aggregatorsEnd

keywordsStart
Expand Down Expand Up @@ -209,15 +212,16 @@ var key = map[string]itemType{
"unless": itemLUnless,

// Aggregators.
"sum": itemSum,
"avg": itemAvg,
"count": itemCount,
"min": itemMin,
"max": itemMax,
"stddev": itemStddev,
"stdvar": itemStdvar,
"topk": itemTopK,
"bottomk": itemBottomK,
"sum": itemSum,
"avg": itemAvg,
"count": itemCount,
"min": itemMin,
"max": itemMax,
"stddev": itemStddev,
"stdvar": itemStdvar,
"topk": itemTopK,
"bottomk": itemBottomK,
"count_values": itemCountValues,

// Keywords.
"alert": itemAlert,
Expand Down
3 changes: 3 additions & 0 deletions promql/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -1052,6 +1052,9 @@ func (p *parser) checkType(node Node) (typ model.ValueType) {
if n.Op == itemTopK || n.Op == itemBottomK {
p.expectType(n.Param, model.ValScalar, "aggregation parameter")
}
if n.Op == itemCountValues {
p.expectType(n.Param, model.ValString, "aggregation parameter")
}

case *BinaryExpr:
lt := p.checkType(n.LHS)
Expand Down
16 changes: 16 additions & 0 deletions promql/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1213,6 +1213,18 @@ var testExpr = []struct {
},
Param: &NumberLiteral{5},
},
}, {
input: "count_values(\"value\", some_metric)",
expected: &AggregateExpr{
Op: itemCountValues,
Expr: &VectorSelector{
Name: "some_metric",
LabelMatchers: metric.LabelMatchers{
{Type: metric.Equal, Name: model.MetricNameLabel, Value: "some_metric"},
},
},
Param: &StringLiteral{"value"},
},
}, {
input: `sum some_metric by (test)`,
fail: true,
Expand Down Expand Up @@ -1257,6 +1269,10 @@ var testExpr = []struct {
input: `topk(some_metric, other_metric)`,
fail: true,
errMsg: "parse error at char 32: expected type scalar in aggregation parameter, got vector",
}, {
input: `count_values(5, other_metric)`,
fail: true,
errMsg: "parse error at char 30: expected type string in aggregation parameter, got scalar",
},
// Test function calls.
{
Expand Down
3 changes: 3 additions & 0 deletions promql/printer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ func TestExprString(t *testing.T) {
{
in: `topk(5, task:errors:rate10s{job="s"})`,
},
{
in: `count_values("value", task:errors:rate10s{job="s"})`,
},
{
in: `a - ON(b) c`,
},
Expand Down
37 changes: 37 additions & 0 deletions promql/testdata/aggregators.test
Original file line number Diff line number Diff line change
Expand Up @@ -183,3 +183,40 @@ eval_ordered instant at 50m bottomk(3, http_requests{job="api-server",group="pro
http_requests{job="api-server", instance="0", group="production"} 100
http_requests{job="api-server", instance="1", group="production"} 200
http_requests{job="api-server", instance="2", group="production"} NaN

clear

# Tests for count_values.
load 5m
version{job="api-server", instance="0", group="production"} 6
version{job="api-server", instance="1", group="production"} 6
version{job="api-server", instance="2", group="production"} 6
version{job="api-server", instance="0", group="canary"} 8
version{job="api-server", instance="1", group="canary"} 8
version{job="app-server", instance="0", group="production"} 6
version{job="app-server", instance="1", group="production"} 6
version{job="app-server", instance="0", group="canary"} 7
version{job="app-server", instance="1", group="canary"} 7

eval instant at 5m count_values("version", version)
{version="6"} 5
{version="7"} 2
{version="8"} 2

eval instant at 5m count_values without (instance)("version", version)
{job="api-server", group="production", version="6"} 3
{job="api-server", group="canary", version="8"} 2
{job="app-server", group="production", version="6"} 2
{job="app-server", group="canary", version="7"} 2

# Overwrite label with output. Don't do this.
eval instant at 5m count_values without (instance)("job", version)
{job="6", group="production"} 5
{job="8", group="canary"} 2
{job="7", group="canary"} 2

# Overwrite label with output. Don't do this.
eval instant at 5m count_values by (job, group)("job", version)
{job="6", group="production"} 5
{job="8", group="canary"} 2
{job="7", group="canary"} 2

0 comments on commit 1669073

Please sign in to comment.