diff --git a/pkg/proxystorage/proxy.go b/pkg/proxystorage/proxy.go index 0a60b00e8..b2e961c4d 100644 --- a/pkg/proxystorage/proxy.go +++ b/pkg/proxystorage/proxy.go @@ -25,6 +25,8 @@ import ( "github.com/jacksontj/promxy/pkg/servergroup" ) +const MetricNameWorkaroundLabel = "__name" + type proxyStorageState struct { sgs []*servergroup.ServerGroup client promclient.API @@ -296,6 +298,51 @@ func (p *ProxyStorage) NodeReplacer(ctx context.Context, s *promql.EvalStmt, nod // Convert avg into sum() / count() case promql.ItemAvg: + + nameIncluded := false + for _, g := range n.Grouping { + if g == model.MetricNameLabel { + nameIncluded = true + } + } + + if nameIncluded { + replacedGrouping := make([]string, len(n.Grouping)) + for i, g := range n.Grouping { + if g == model.MetricNameLabel { + replacedGrouping[i] = MetricNameWorkaroundLabel + } else { + replacedGrouping[i] = g + } + } + + return &promql.AggregateExpr{ + Op: promql.ItemMax, + Expr: PreserveLabel(&promql.BinaryExpr{ + Op: promql.ItemDIV, + LHS: &promql.AggregateExpr{ + Op: promql.ItemSum, + Expr: PreserveLabel(CloneExpr(n.Expr), model.MetricNameLabel, MetricNameWorkaroundLabel), + Param: n.Param, + Grouping: replacedGrouping, + Without: n.Without, + }, + + RHS: &promql.AggregateExpr{ + Op: promql.ItemCount, + Expr: PreserveLabel(CloneExpr(n.Expr), model.MetricNameLabel, MetricNameWorkaroundLabel), + Param: n.Param, + Grouping: replacedGrouping, + Without: n.Without, + }, + VectorMatching: &promql.VectorMatching{Card: promql.CardOneToOne}, + }, MetricNameWorkaroundLabel, model.MetricNameLabel), + Grouping: n.Grouping, + Without: n.Without, + }, nil + + } + // Replace with sum() / count() return &promql.BinaryExpr{ Op: promql.ItemDIV, diff --git a/pkg/proxystorage/util.go b/pkg/proxystorage/util.go index b22f97299..16f1c7d00 100644 --- a/pkg/proxystorage/util.go +++ b/pkg/proxystorage/util.go @@ -115,3 +115,9 @@ func CloneExpr(expr promql.Expr) (newExpr promql.Expr) { newExpr, _ = promql.ParseExpr(expr.String()) return } + +// PreserveLabel wraps the input expression with a label replace in order to preserve the metadata through binary expressions +func PreserveLabel(expr promql.Expr, srcLabel string, dstLabel string) (relabelExpress promql.Expr) { + relabelExpress, _ = promql.ParseExpr(fmt.Sprintf("label_replace(%s,`%s`,`$1`,`%s`,`(.*)`)", expr.String(), dstLabel, srcLabel)) + return relabelExpress +} diff --git a/test/testdata/issue_274.test b/test/testdata/issue_274.test new file mode 100644 index 000000000..e8f477ff4 --- /dev/null +++ b/test/testdata/issue_274.test @@ -0,0 +1,18 @@ +# Test for https://github.com/jacksontj/promxy/issues/274 +load 5m + metric_1{a="1"} 1 + metric_10{a="10"} 10 + metric_1000{a="1000"} 1000 + metric_10000{a="10000"} 10000 + +eval instant at 5m avg({__name__=~"metric_.*"}) by (a) + {a="1"} 1 + {a="10"} 10 + {a="1000"} 1000 + {a="10000"} 10000 + +eval instant at 5m avg({__name__=~"metric_.*"}) by (__name__) + metric_1{} 1 + metric_10{} 10 + metric_1000{} 1000 + metric_10000{} 10000