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

opt: factor out common panic recovery into helper function #91752

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
18 changes: 5 additions & 13 deletions pkg/sql/explain_plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/cockroachdb/cockroach/pkg/sql/catalog/colinfo"
"github.com/cockroachdb/cockroach/pkg/sql/colflow"
"github.com/cockroachdb/cockroach/pkg/sql/execinfrapb"
"github.com/cockroachdb/cockroach/pkg/sql/opt"
"github.com/cockroachdb/cockroach/pkg/sql/opt/cat"
"github.com/cockroachdb/cockroach/pkg/sql/opt/exec"
"github.com/cockroachdb/cockroach/pkg/sql/opt/exec/explain"
Expand All @@ -28,7 +29,6 @@ import (
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
"github.com/cockroachdb/cockroach/pkg/sql/sessiondatapb"
"github.com/cockroachdb/cockroach/pkg/util/buildutil"
"github.com/cockroachdb/cockroach/pkg/util/errorutil"
"github.com/cockroachdb/errors"
)

Expand Down Expand Up @@ -178,19 +178,11 @@ func emitExplain(
) (err error) {
// Guard against bugs in the explain code.
defer func() {
if r := recover(); r != nil {
// This code allows us to propagate internal and runtime errors without
// having to add error checks everywhere throughout the code. This is only
// possible because the code does not update shared state and does not
// manipulate locks.
// Note that we don't catch anything in debug builds, so that failures are
// more visible.
if ok, e := errorutil.ShouldCatch(r); ok && !buildutil.CrdbTestBuild {
// Note that we don't catch anything in debug builds, so that failures are
// more visible.
if !buildutil.CrdbTestBuild {
if e := opt.CatchOptimizerError(); e != nil {
err = e
} else {
// Other panic objects can't be considered "safe" and thus are
// propagated as crashes that terminate the session.
panic(r)
}
}
}()
Expand Down
1 change: 1 addition & 0 deletions pkg/sql/opt/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "opt",
srcs = [
"catch.go",
"colset.go",
"column_meta.go",
"constants.go",
Expand Down
33 changes: 33 additions & 0 deletions pkg/sql/opt/catch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package opt

import (
"runtime"

"github.com/cockroachdb/errors"
)

// CatchOptimizerError catches any runtime panics from optimizer functions and
// returns them as errors. This allows the optimizer to propagate errors
// internally as panics without adding error checks everywhere. This is only
// possible because the optimizer code does not update shared state and does not
// manipulate locks.
func CatchOptimizerError() error {
r := recover()
if r == nil {
return nil
}
err, ok := r.(error)
if !ok {
// Not an error object. For serious internal errors e.g. in the scheduler,
// bad goroutine state, allocator problem etc, the go runtime throws a
// string which does not implement error. So in this case we suspect we are
// not able to recover, and must crash.
panic(r)
}
if errors.HasInterface(err, (*runtime.Error)(nil)) {
// Convert runtime errors to assertion failures, which include stacks
// and get reported to Sentry.
return errors.HandleAsAssertionFailure(err)
}
return err
}
2 changes: 0 additions & 2 deletions pkg/sql/opt/exec/execbuilder/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,7 @@ go_library(
"//pkg/util",
"//pkg/util/buildutil",
"//pkg/util/encoding",
"//pkg/util/errorutil",
"//pkg/util/errorutil/unimplemented",
"//pkg/util/log",
"//pkg/util/timeutil",
"//pkg/util/treeprinter",
"@com_github_cockroachdb_errors//:errors",
Expand Down
13 changes: 2 additions & 11 deletions pkg/sql/opt/exec/execbuilder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import (
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
"github.com/cockroachdb/cockroach/pkg/sql/types"
"github.com/cockroachdb/cockroach/pkg/util"
"github.com/cockroachdb/cockroach/pkg/util/errorutil"
"github.com/cockroachdb/errors"
"github.com/cockroachdb/redact"
)
Expand Down Expand Up @@ -282,16 +281,8 @@ func (b *Builder) wrapFunction(fnName string) tree.ResolvableFunctionReference {

func (b *Builder) build(e opt.Expr) (_ execPlan, err error) {
defer func() {
if r := recover(); r != nil {
// This code allows us to propagate errors without adding lots of checks
// for `if err != nil` throughout the construction code. This is only
// possible because the code does not update shared state and does not
// manipulate locks.
if ok, e := errorutil.ShouldCatch(r); ok {
err = e
} else {
panic(r)
}
if e := opt.CatchOptimizerError(); e != nil {
err = e
}
}()

Expand Down
22 changes: 2 additions & 20 deletions pkg/sql/opt/exec/execbuilder/relational.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,7 @@ import (
"github.com/cockroachdb/cockroach/pkg/util"
"github.com/cockroachdb/cockroach/pkg/util/buildutil"
"github.com/cockroachdb/cockroach/pkg/util/encoding"
"github.com/cockroachdb/cockroach/pkg/util/errorutil"
"github.com/cockroachdb/cockroach/pkg/util/errorutil/unimplemented"
"github.com/cockroachdb/cockroach/pkg/util/log"
"github.com/cockroachdb/cockroach/pkg/util/timeutil"
"github.com/cockroachdb/errors"
"github.com/cockroachdb/redact"
Expand Down Expand Up @@ -1070,24 +1068,8 @@ func (b *Builder) buildApplyJoin(join memo.RelExpr) (execPlan, error) {
var o xform.Optimizer
planRightSideFn := func(ctx context.Context, ef exec.Factory, leftRow tree.Datums) (_ exec.Plan, err error) {
defer func() {
if r := recover(); r != nil {
// This code allows us to propagate internal errors without having to add
// error checks everywhere throughout the code. This is only possible
// because the code does not update shared state and does not manipulate
// locks.
//
// This is the same panic-catching logic that exists in
// o.Optimize() below. It's required here because it's possible
// for factory functions to panic below, like
// CopyAndReplaceDefault.
if ok, e := errorutil.ShouldCatch(r); ok {
err = e
log.VEventf(ctx, 1, "%v", err)
} else {
// Other panic objects can't be considered "safe" and thus are
// propagated as crashes that terminate the session.
panic(r)
}
if e := opt.CatchOptimizerError(); e != nil {
err = e
}
}()

Expand Down
1 change: 0 additions & 1 deletion pkg/sql/opt/exec/explain/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ go_library(
"//pkg/sql/sem/tree",
"//pkg/sql/types",
"//pkg/util",
"//pkg/util/errorutil",
"//pkg/util/humanizeutil",
"//pkg/util/timeutil",
"//pkg/util/treeprinter",
Expand Down
16 changes: 3 additions & 13 deletions pkg/sql/opt/exec/explain/plan_gist_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/cockroachdb/cockroach/pkg/sql/catalog/colinfo"
"github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb"
"github.com/cockroachdb/cockroach/pkg/sql/inverted"
"github.com/cockroachdb/cockroach/pkg/sql/opt"
"github.com/cockroachdb/cockroach/pkg/sql/opt/cat"
"github.com/cockroachdb/cockroach/pkg/sql/opt/constraint"
"github.com/cockroachdb/cockroach/pkg/sql/opt/exec"
Expand All @@ -30,7 +31,6 @@ import (
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
"github.com/cockroachdb/cockroach/pkg/sql/types"
"github.com/cockroachdb/cockroach/pkg/util"
"github.com/cockroachdb/cockroach/pkg/util/errorutil"
"github.com/cockroachdb/errors"
)

Expand Down Expand Up @@ -155,18 +155,8 @@ func (f *PlanGistFactory) PlanGist() PlanGist {
// DecodePlanGistToRows converts a gist to a logical plan and returns the rows.
func DecodePlanGistToRows(gist string, catalog cat.Catalog) (_ []string, retErr error) {
defer func() {
if r := recover(); r != nil {
// This code allows us to propagate internal errors without having
// to add error checks everywhere throughout the code. This is only
// possible because the code does not update shared state and does
// not manipulate locks.
if ok, e := errorutil.ShouldCatch(r); ok {
retErr = e
} else {
// Other panic objects can't be considered "safe" and thus are
// propagated as crashes that terminate the session.
panic(r)
}
if e := opt.CatchOptimizerError(); e != nil {
retErr = e
}
}()

Expand Down
14 changes: 5 additions & 9 deletions pkg/sql/opt/exec/explain/result_columns.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ package explain
import (
"github.com/cockroachdb/cockroach/pkg/sql/catalog/colinfo"
"github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb"
"github.com/cockroachdb/cockroach/pkg/sql/opt"
"github.com/cockroachdb/cockroach/pkg/sql/opt/cat"
"github.com/cockroachdb/cockroach/pkg/sql/opt/exec"
"github.com/cockroachdb/cockroach/pkg/sql/types"
"github.com/cockroachdb/cockroach/pkg/util/errorutil"
"github.com/cockroachdb/errors"
)

Expand All @@ -26,15 +26,11 @@ import (
func getResultColumns(
op execOperator, args interface{}, inputs ...colinfo.ResultColumns,
) (out colinfo.ResultColumns, err error) {
// If we have a bug in the code below, it's easily possible to hit panic
// (like out-of-bounds). Catch these here and return as an error.
defer func() {
if r := recover(); r != nil {
// If we have a bug in the code below, it's easily possible to hit panic
// (like out-of-bounds). Catch these here and return as an error.
if ok, e := errorutil.ShouldCatch(r); ok {
err = e
} else {
panic(r)
}
if e := opt.CatchOptimizerError(); e != nil {
err = e
}
}()

Expand Down
12 changes: 2 additions & 10 deletions pkg/sql/opt/norm/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -303,16 +303,8 @@ func (f *Factory) CopyWithoutAssigningPlaceholders(e opt.Expr) opt.Expr {
// exploration phase can begin.
func (f *Factory) AssignPlaceholders(from *memo.Memo) (err error) {
defer func() {
if r := recover(); r != nil {
// This code allows us to propagate errors without adding lots of checks
// for `if err != nil` throughout the construction code. This is only
// possible because the code does not update shared state and does not
// manipulate locks.
if ok, e := errorutil.ShouldCatch(r); ok {
err = e
} else {
panic(r)
}
if e := opt.CatchOptimizerError(); e != nil {
err = e
}
}()

Expand Down
1 change: 0 additions & 1 deletion pkg/sql/opt/optbuilder/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ go_library(
"//pkg/sql/sqltelemetry",
"//pkg/sql/types",
"//pkg/util",
"//pkg/util/errorutil",
"//pkg/util/errorutil/unimplemented",
"//pkg/util/log",
"@com_github_cockroachdb_errors//:errors",
Expand Down
15 changes: 3 additions & 12 deletions pkg/sql/opt/optbuilder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import (
"github.com/cockroachdb/cockroach/pkg/sql/sem/eval"
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
"github.com/cockroachdb/cockroach/pkg/sql/types"
"github.com/cockroachdb/cockroach/pkg/util/errorutil"
"github.com/cockroachdb/cockroach/pkg/util/errorutil/unimplemented"
"github.com/cockroachdb/cockroach/pkg/util/log"
"github.com/cockroachdb/errors"
Expand Down Expand Up @@ -184,17 +183,9 @@ func (b *Builder) Build() (err error) {
log.VEventf(b.ctx, 1, "optbuilder start")
defer log.VEventf(b.ctx, 1, "optbuilder finish")
defer func() {
if r := recover(); r != nil {
// This code allows us to propagate errors without adding lots of checks
// for `if err != nil` throughout the construction code. This is only
// possible because the code does not update shared state and does not
// manipulate locks.
if ok, e := errorutil.ShouldCatch(r); ok {
err = e
log.VEventf(b.ctx, 1, "%v", err)
} else {
panic(r)
}
if e := opt.CatchOptimizerError(); e != nil {
err = e
log.VEventf(b.ctx, 1, "%v", err)
}
}()

Expand Down
9 changes: 2 additions & 7 deletions pkg/sql/opt/optbuilder/fk_cascade.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (
"github.com/cockroachdb/cockroach/pkg/sql/sem/eval"
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
"github.com/cockroachdb/cockroach/pkg/util"
"github.com/cockroachdb/cockroach/pkg/util/errorutil"
"github.com/cockroachdb/errors"
)

Expand Down Expand Up @@ -895,12 +894,8 @@ func buildCascadeHelper(

// Enact panic handling similar to Builder.Build().
defer func() {
if r := recover(); r != nil {
if ok, e := errorutil.ShouldCatch(r); ok {
err = e
} else {
panic(r)
}
if e := opt.CatchOptimizerError(); e != nil {
err = e
}
}()

Expand Down
13 changes: 2 additions & 11 deletions pkg/sql/opt/optbuilder/scalar.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import (
"github.com/cockroachdb/cockroach/pkg/sql/sqlerrors"
"github.com/cockroachdb/cockroach/pkg/sql/sqltelemetry"
"github.com/cockroachdb/cockroach/pkg/sql/types"
"github.com/cockroachdb/cockroach/pkg/util/errorutil"
"github.com/cockroachdb/cockroach/pkg/util/errorutil/unimplemented"
"github.com/cockroachdb/errors"
"github.com/cockroachdb/redact"
Expand Down Expand Up @@ -1017,16 +1016,8 @@ func NewScalar(
// expression equivalent to expr.
func (sb *ScalarBuilder) Build(expr tree.Expr) (err error) {
defer func() {
if r := recover(); r != nil {
// This code allows us to propagate errors without adding lots of checks
// for `if err != nil` throughout the construction code. This is only
// possible because the code does not update shared state and does not
// manipulate locks.
if ok, e := errorutil.ShouldCatch(r); ok {
err = e
} else {
panic(r)
}
if e := opt.CatchOptimizerError(); e != nil {
err = e
}
}()

Expand Down
1 change: 0 additions & 1 deletion pkg/sql/opt/xform/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ go_library(
"//pkg/util",
"//pkg/util/buildutil",
"//pkg/util/cancelchecker",
"//pkg/util/errorutil",
"//pkg/util/log",
"//pkg/util/treeprinter",
"@com_github_cockroachdb_errors//:errors",
Expand Down
17 changes: 3 additions & 14 deletions pkg/sql/opt/xform/optimizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import (
"github.com/cockroachdb/cockroach/pkg/sql/sem/eval"
"github.com/cockroachdb/cockroach/pkg/util"
"github.com/cockroachdb/cockroach/pkg/util/cancelchecker"
"github.com/cockroachdb/cockroach/pkg/util/errorutil"
"github.com/cockroachdb/cockroach/pkg/util/log"
"github.com/cockroachdb/errors"
)
Expand Down Expand Up @@ -240,19 +239,9 @@ func (o *Optimizer) Optimize() (_ opt.Expr, err error) {
log.VEventf(o.ctx, 1, "optimize start")
defer log.VEventf(o.ctx, 1, "optimize finish")
defer func() {
if r := recover(); r != nil {
// This code allows us to propagate internal errors without having to add
// error checks everywhere throughout the code. This is only possible
// because the code does not update shared state and does not manipulate
// locks.
if ok, e := errorutil.ShouldCatch(r); ok {
err = e
log.VEventf(o.ctx, 1, "%v", err)
} else {
// Other panic objects can't be considered "safe" and thus are
// propagated as crashes that terminate the session.
panic(r)
}
if e := opt.CatchOptimizerError(); e != nil {
err = e
log.VEventf(o.ctx, 1, "%v", err)
}
}()

Expand Down
Loading