Skip to content

Commit 4a3f088

Browse files
committed
logicalplan: distributed optimizer fuzzing
We should only have remote executions that maintain the partition. Add a fuzzing test to check that invariant. Signed-off-by: Michael Hoffmann <mhoffmann@cloudflare.com>
1 parent da6a186 commit 4a3f088

File tree

2 files changed

+86
-1
lines changed

2 files changed

+86
-1
lines changed

logicalplan/distribute.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,18 @@ func (r RemoteExecution) ReturnType() parser.ValueType { return r.Query.ReturnTy
102102

103103
// Deduplicate is a logical plan which deduplicates samples from multiple RemoteExecutions.
104104
type Deduplicate struct {
105-
LeafNode
106105
Expressions RemoteExecutions
107106
}
108107

108+
func (r Deduplicate) Children() []*Node {
109+
children := make([]*Node, len(r.Expressions))
110+
for i := range r.Expressions {
111+
var n Node = r.Expressions[i]
112+
children[i] = &n
113+
}
114+
return children
115+
}
116+
109117
func (r Deduplicate) Clone() Node {
110118
clone := r
111119
clone.Expressions = make(RemoteExecutions, len(r.Expressions))

logicalplan/distribute_test.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ package logicalplan
55

66
import (
77
"math"
8+
"math/rand"
89
"regexp"
910
"testing"
1011
"time"
1112

1213
"github.com/thanos-io/promql-engine/api"
1314
"github.com/thanos-io/promql-engine/query"
1415

16+
"github.com/cortexproject/promqlsmith"
1517
"github.com/efficientgo/core/testutil"
1618
"github.com/prometheus/prometheus/model/labels"
1719
"github.com/prometheus/prometheus/promql/parser"
@@ -1083,3 +1085,78 @@ func newEngineMock(mint, maxt int64, labelSets []labels.Labels) *engineMock {
10831085
func newEngineMockWithExplicitPartition(mint, maxt int64, labelSets, partitionLabelSets []labels.Labels) *engineMock {
10841086
return &engineMock{minT: mint, maxT: maxt, labelSets: labelSets, partitionLabelSets: partitionLabelSets}
10851087
}
1088+
1089+
func FuzzDistributedExecutionPreservesPartitionLabels(f *testing.F) {
1090+
f.Add(int64(0))
1091+
f.Fuzz(func(t *testing.T, seed int64) {
1092+
rnd := rand.New(rand.NewSource(seed))
1093+
1094+
engines := []api.RemoteEngine{
1095+
newEngineMock(math.MinInt64, math.MaxInt64, []labels.Labels{labels.FromStrings("region", "east")}),
1096+
newEngineMock(math.MinInt64, math.MaxInt64, []labels.Labels{labels.FromStrings("region", "west")}),
1097+
}
1098+
optimizers := []Optimizer{
1099+
DistributedExecutionOptimizer{Endpoints: api.NewStaticEndpoints(engines)},
1100+
}
1101+
engineLabels := map[string]struct{}{"region": {}}
1102+
1103+
lbls := []labels.Labels{
1104+
labels.FromStrings("__name__", "http_requests_total", "pod", "nginx-1", "region", "east"),
1105+
labels.FromStrings("__name__", "http_requests_total", "pod", "nginx-2", "region", "west"),
1106+
}
1107+
1108+
psOpts := []promqlsmith.Option{
1109+
promqlsmith.WithEnableOffset(false),
1110+
promqlsmith.WithEnableAtModifier(false),
1111+
promqlsmith.WithEnabledAggrs([]parser.ItemType{
1112+
parser.SUM, parser.MIN, parser.MAX, parser.AVG, parser.GROUP,
1113+
parser.COUNT, parser.QUANTILE, parser.STDDEV, parser.STDVAR,
1114+
parser.COUNT_VALUES, parser.TOPK, parser.BOTTOMK,
1115+
}),
1116+
promqlsmith.WithEnableVectorMatching(true),
1117+
}
1118+
ps := promqlsmith.New(rnd, lbls, psOpts...)
1119+
1120+
opts := &query.Options{
1121+
Start: time.Unix(0, 0),
1122+
End: time.Unix(3600, 0),
1123+
Step: time.Minute,
1124+
}
1125+
for range testRuns {
1126+
expr := ps.WalkRangeQuery()
1127+
exprStr := expr.Pretty(0)
1128+
1129+
parsed, err := parser.ParseExpr(exprStr)
1130+
if err != nil {
1131+
continue
1132+
}
1133+
1134+
plan, err := NewFromAST(parsed, opts, PlanOptions{})
1135+
if err != nil {
1136+
continue
1137+
}
1138+
1139+
optimizedPlan, _ := plan.Optimize(optimizers)
1140+
root := optimizedPlan.Root()
1141+
1142+
Traverse(&root, func(node *Node) {
1143+
remote, ok := (*node).(RemoteExecution)
1144+
if !ok {
1145+
return
1146+
}
1147+
if isAbsent(&remote.Query) {
1148+
return
1149+
}
1150+
if !preservesPartitionLabels(remote.Query, engineLabels) {
1151+
t.Errorf(
1152+
"remote query does not preserve partition labels\n"+
1153+
" original: %s\n"+
1154+
" optimized: %s\n"+
1155+
" remote query: %s",
1156+
exprStr, root.String(), remote.Query.String(),
1157+
)
1158+
}
1159+
})
1160+
}
1161+
})
1162+
}

0 commit comments

Comments
 (0)