Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

constraint: perform cancel checking when combining constraints #111979

Merged
Merged
Show file tree
Hide file tree
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
3 changes: 3 additions & 0 deletions pkg/sql/opt/constraint/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,8 @@ go_test(
"//pkg/util/intsets",
"//pkg/util/leaktest",
"//pkg/util/randutil",
"//pkg/util/timeutil",
"@com_github_cockroachdb_errors//:errors",
"@com_github_stretchr_testify//require",
],
)
14 changes: 13 additions & 1 deletion pkg/sql/opt/constraint/constraint.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
"github.com/cockroachdb/errors"
)

const CancelCheckInterval = 16

// Constraint specifies the possible set of values that one or more columns
// will have in the result set. If this is a single column constraint, then
// that column's value will always be part of one of the spans in this
Expand Down Expand Up @@ -344,7 +346,7 @@ func (c *Constraint) findIntersectingSpan(keyCtx *KeyContext, sp *Span) (_ *Span
// c: /a/b: [/1 - /2] [/4 - /4]
// other: /b: [/5 - /5]
// result: /a/b: [/1/5 - /2/5] [/4/5 - /4/5]
func (c *Constraint) Combine(evalCtx *eval.Context, other *Constraint) {
func (c *Constraint) Combine(evalCtx *eval.Context, other *Constraint, checkCancellation func()) {
if !other.Columns.IsStrictSuffixOf(&c.Columns) {
// Note: we don't want to let the c and other pointers escape by passing
// them directly to Sprintf.
Expand All @@ -366,7 +368,15 @@ func (c *Constraint) Combine(evalCtx *eval.Context, other *Constraint) {
var resultInitialized bool
keyCtx := KeyContext{Columns: c.Columns, EvalCtx: evalCtx}

numIterations := 0
cancelCheck := func() {
numIterations++
if checkCancellation != nil && (numIterations%CancelCheckInterval) == 0 {
checkCancellation()
}
}
for i := 0; i < c.Spans.Count(); i++ {
cancelCheck()
sp := *c.Spans.Get(i)

startLen, endLen := sp.start.Length(), sp.end.Length()
Expand Down Expand Up @@ -402,6 +412,7 @@ func (c *Constraint) Combine(evalCtx *eval.Context, other *Constraint) {
}
}
for j := 0; j < other.Spans.Count(); j++ {
cancelCheck()
extSp := other.Spans.Get(j)
var newSp Span
newSp.Init(
Expand Down Expand Up @@ -450,6 +461,7 @@ func (c *Constraint) Combine(evalCtx *eval.Context, other *Constraint) {
resultInitialized = true
result.Alloc(c.Spans.Count())
for j := 0; j < i; j++ {
cancelCheck()
result.Append(c.Spans.Get(j))
}
}
Expand Down
88 changes: 87 additions & 1 deletion pkg/sql/opt/constraint/constraint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ package constraint
import (
"fmt"
"testing"
"time"

"github.com/cockroachdb/cockroach/pkg/settings/cluster"
"github.com/cockroachdb/cockroach/pkg/sql/opt"
Expand All @@ -22,6 +23,9 @@ import (
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
"github.com/cockroachdb/cockroach/pkg/util/intsets"
"github.com/cockroachdb/cockroach/pkg/util/leaktest"
"github.com/cockroachdb/cockroach/pkg/util/timeutil"
"github.com/cockroachdb/errors"
"github.com/stretchr/testify/require"
)

func TestConstraintUnion(t *testing.T) {
Expand Down Expand Up @@ -452,14 +456,96 @@ func TestConstraintCombine(t *testing.T) {
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
a := ParseConstraint(&evalCtx, tc.a)
b := ParseConstraint(&evalCtx, tc.b)
a.Combine(&evalCtx, &b)
a.Combine(&evalCtx, &b, nil /* checkCancellation */)
if res := a.String(); res != tc.e {
t.Errorf("expected\n %s; got\n %s", tc.e, res)
}
})
}
}

// TestCancelLargeConstraintCombine tests that combining constraints can be
// successfully cancelled with a cancel function.
func TestCancelLargeConstraintCombine(t *testing.T) {
st := cluster.MakeTestingClusterSettings()
evalCtx := eval.MakeTestingEvalContext(st)

testData := []struct {
a, b, c, d, e, f, g, h, j, k, l, m, n, o, p, q string
}{
{
a: "/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16: [/10 - /10] [/20 - /20] [/30 - /30] [/40 - /40] [/50 - /50] [/60 - /60] [/70 - /70] [/80 - /80] [/90 - /90]",
b: "/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16: [/10 - /10] [/20 - /20] [/30 - /30] [/40 - /40] [/50 - /50] [/60 - /60] [/70 - /70] [/80 - /80] [/90 - /90]",
c: "/3/4/5/6/7/8/9/10/11/12/13/14/15/16: [/10 - /10] [/20 - /20] [/30 - /30] [/40 - /40] [/50 - /50] [/60 - /60] [/70 - /70] [/80 - /80] [/90 - /90]",
d: "/4/5/6/7/8/9/10/11/12/13/14/15/16: [/10 - /10] [/20 - /20] [/30 - /30] [/40 - /40] [/50 - /50] [/60 - /60] [/70 - /70] [/80 - /80] [/90 - /90]",
e: "/5/6/7/8/9/10/11/12/13/14/15/16: [/10 - /10] [/20 - /20] [/30 - /30] [/40 - /40] [/50 - /50] [/60 - /60] [/70 - /70] [/80 - /80] [/90 - /90]",
f: "/6/7/8/9/10/11/12/13/14/15/16: [/10 - /10] [/20 - /20] [/30 - /30] [/40 - /40] [/50 - /50] [/60 - /60] [/70 - /70] [/80 - /80] [/90 - /90]",
g: "/7/8/9/10/11/12/13/14/15/16: [/10 - /10] [/20 - /20] [/30 - /30] [/40 - /40] [/50 - /50] [/60 - /60] [/70 - /70] [/80 - /80] [/90 - /90]",
h: "/8/9/10/11/12/13/14/15/16: [/10 - /10] [/20 - /20] [/30 - /30] [/40 - /40] [/50 - /50] [/60 - /60] [/70 - /70] [/80 - /80] [/90 - /90]",
j: "/9/10/11/12/13/14/15/16: [/10 - /10] [/20 - /20] [/30 - /30] [/40 - /40] [/50 - /50] [/60 - /60] [/70 - /70] [/80 - /80] [/90 - /90]",
k: "/10/11/12/13/14/15/16: [/10 - /10] [/20 - /20] [/30 - /30] [/40 - /40] [/50 - /50] [/60 - /60] [/70 - /70] [/80 - /80] [/90 - /90]",
l: "/11/12/13/14/15/16: [/10 - /10] [/20 - /20] [/30 - /30] [/40 - /40] [/50 - /50] [/60 - /60] [/70 - /70] [/80 - /80] [/90 - /90]",
m: "/12/13/14/15/16: [/10 - /10] [/20 - /20] [/30 - /30] [/40 - /40] [/50 - /50] [/60 - /60] [/70 - /70] [/80 - /80] [/90 - /90]",
n: "/13/14/15/16: [/10 - /10] [/20 - /20] [/30 - /30] [/40 - /40] [/50 - /50] [/60 - /60] [/70 - /70] [/80 - /80] [/90 - /90]",
o: "/14/15/16: [/10 - /10] [/20 - /20] [/30 - /30] [/40 - /40] [/50 - /50] [/60 - /60] [/70 - /70] [/80 - /80] [/90 - /90]",
p: "/15/16: [/10 - /10] [/20 - /20] [/30 - /30] [/40 - /40] [/50 - /50] [/60 - /60] [/70 - /70] [/80 - /80] [/90 - /90]",
q: "/16: [/10 - /10] [/20 - /20] [/30 - /30] [/40 - /40] [/50 - /50] [/60 - /60] [/70 - /70] [/80 - /80] [/90 - /90]",
},
}

for i, tc := range testData {
iterations := 0
cancelFunc := func() {
iterations++
if iterations > 2000 {
panic(errors.AssertionFailedf("We successfully cancelled the operation!"))
}
}
testFunc := func(constraint1, constraint2 *Constraint) {
ts := timeutil.Now()
constraint1.Combine(&evalCtx, constraint2, cancelFunc)
if d := timeutil.Since(ts); d > time.Second {
t.Errorf("expected cancellation to occur in under 1 second, but took %v", d)
}
}
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
a := ParseConstraint(&evalCtx, tc.a)
b := ParseConstraint(&evalCtx, tc.b)
c := ParseConstraint(&evalCtx, tc.c)
d := ParseConstraint(&evalCtx, tc.d)
e := ParseConstraint(&evalCtx, tc.e)
f := ParseConstraint(&evalCtx, tc.f)
g := ParseConstraint(&evalCtx, tc.g)
h := ParseConstraint(&evalCtx, tc.h)
j := ParseConstraint(&evalCtx, tc.j)
k := ParseConstraint(&evalCtx, tc.k)
l := ParseConstraint(&evalCtx, tc.l)
m := ParseConstraint(&evalCtx, tc.m)
n := ParseConstraint(&evalCtx, tc.n)
o := ParseConstraint(&evalCtx, tc.o)
p := ParseConstraint(&evalCtx, tc.p)
q := ParseConstraint(&evalCtx, tc.q)
testFunc(&a, &b)
testFunc(&c, &d)
testFunc(&e, &f)
testFunc(&g, &h)
testFunc(&j, &k)
testFunc(&l, &m)
testFunc(&n, &o)
testFunc(&p, &q)
testFunc(&a, &c)
testFunc(&e, &g)
testFunc(&j, &l)
testFunc(&n, &p)
// The next Combine call will panic within a second or two.
require.Panics(t, func() { testFunc(&a, &e) })
// This Combine call panics right away since the iterations target has
// already been reached.
require.Panics(t, func() { testFunc(&j, &n) })
})
}
}

func TestConsolidateSpans(t *testing.T) {
defer leaktest.AfterTest(t)()
st := cluster.MakeTestingClusterSettings()
Expand Down
4 changes: 2 additions & 2 deletions pkg/sql/opt/idxconstraint/index_constraints.go
Original file line number Diff line number Diff line change
Expand Up @@ -845,11 +845,11 @@ func (c *indexConstraintCtx) makeSpansForAnd(
}
ofsC.IntersectWith(c.evalCtx, &exprConstraint)
}
out.Combine(c.evalCtx, &ofsC)
out.Combine(c.evalCtx, &ofsC, c.checkCancellation)
numIterations++
// In case we can't exit this loop, allow the cancel checker to cancel
// this query.
if (numIterations % 16) == 0 {
if (numIterations % constraint.CancelCheckInterval) == 0 {
c.checkCancellation()
}
}
Expand Down