Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions integration/query_fuzz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1980,6 +1980,55 @@ func shouldUseSampleNumComparer(query string) bool {
return false
}

// hasOrVectorFallback reports whether expr contains a sub-expression of the
// shape `<lhs> or vector(<rhs>)`. Such queries diverge between Cortex's
// sharded and unsharded query paths when the LHS has partial time coverage:
// the unsharded engine sees the gap and falls through to vector(...), while
// individual shards may each see (different) coverage and skip the fallback.
// This is the same class of semantic-divergence bug already known for
// `absent`/`absent_over_time`/`scalar` (see #5203, #5204, #5205).
func hasOrVectorFallback(expr parser.Expr) bool {
found := false
parser.Inspect(expr, func(node parser.Node, _ []parser.Node) error {
be, ok := node.(*parser.BinaryExpr)
if !ok || be.Op != parser.LOR {
return nil
}
call, ok := be.RHS.(*parser.Call)
if !ok || call.Func == nil {
return nil
}
if call.Func.Name == "vector" {
found = true
}
return nil
})
return found
}

func TestHasOrVectorFallback(t *testing.T) {
for _, tc := range []struct {
query string
want bool
}{
{`up`, false},
{`vector(1)`, false},
{`up or up`, false},
{`up or vector(1)`, true},
{`(sum(rate(up[1m])) == bool 0) or vector(0)`, true},
// The actual failing case from issue #7547, simplified.
{`(sum without (job) (stddev_over_time(up[4m]) == -up)) or vector(2.0099)`, true},
} {
t.Run(tc.query, func(t *testing.T) {
expr, err := parser.ParseExpr(tc.query)
require.NoError(t, err)
if got := hasOrVectorFallback(expr); got != tc.want {
t.Fatalf("hasOrVectorFallback(%q) = %v, want %v", tc.query, got, tc.want)
}
})
}
}

func isValidQuery(generatedQuery parser.Expr, skipBackwardIncompat bool) bool {
isValid := true
queryStr := generatedQuery.String()
Expand All @@ -1998,6 +2047,11 @@ func isValidQuery(generatedQuery parser.Expr, skipBackwardIncompat bool) bool {
// to avoid false positives in backward compatibility tests.
return false
}
if hasOrVectorFallback(generatedQuery) {
// `or vector(...)` falls through at different timestamps in sharded vs
// unsharded engines when the LHS has partial time coverage; see #7547.
return false
}
if skipBackwardIncompat {
// Skip functions and aggregations whose evaluation semantics changed across
// Prometheus versions embedded in different Cortex releases. These produce
Expand Down
Loading