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

fix(spanner): wrap errors in inline begin transaction to avoid masking #8273

Open
wants to merge 3 commits into
base: main
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
40 changes: 40 additions & 0 deletions spanner/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,22 @@ func (*TransactionOutcomeUnknownError) Error() string { return transactionOutcom
// Unwrap returns the wrapped error (if any).
func (e *TransactionOutcomeUnknownError) Unwrap() error { return e.err }

// InlineBeginTransactionFailedError is wrapped in a Spanner error when the error
// occurred during an inline begin transaction.
type InlineBeginTransactionFailedError struct {
// err is the wrapped error that caused this InlineBeginTransactionFailedError
// error. The wrapped error can be read with the Unwrap method.
err error
}

const inlineBeginTransactionFailedMsg = "failed inline begin transaction"

// Error implements error.Error.
func (*InlineBeginTransactionFailedError) Error() string { return inlineBeginTransactionFailedMsg }

// Unwrap returns the wrapped error (if any).
func (e *InlineBeginTransactionFailedError) Unwrap() error { return e.err }

// Error implements error.Error.
func (e *Error) Error() string {
if e == nil {
Expand Down Expand Up @@ -181,6 +197,30 @@ func toSpannerErrorWithCommitInfo(err error, errorDuringCommit bool) error {
}
}

// toSpannerErrorDuringInlineBegin general Go error to *spanner.Error with additional information
// if the error occurred during an inline begin transaction.
//
// If err is already a *spanner.Error, err is returned unmodified.
func toSpannerErrorDuringInlineBegin(err error) error {
if err == nil {
return nil
}
var se *Error
if errorAs(err, &se) {
return se
}
statusErr := status.Convert(err)
code, desc := statusErr.Code(), statusErr.Message()
desc = fmt.Sprintf("%s, %s", desc, inlineBeginTransactionFailedMsg)
wrapped := &InlineBeginTransactionFailedError{err: err}
return &Error{
code,
toAPIError(wrapped),
desc,
"",
}
}

// ErrCode extracts the canonical error code from a Go error.
func ErrCode(err error) codes.Code {
s, ok := status.FromError(err)
Expand Down
2 changes: 1 addition & 1 deletion spanner/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -1656,7 +1656,7 @@ func isFailedInlineBeginTransaction(err error) bool {
if err == nil {
return false
}
return ErrCode(err) == codes.Internal && strings.Contains(err.Error(), errInlineBeginTransactionFailed().Error())
return strings.Contains(err.Error(), errInlineBeginTransactionFailed().Error())
}

// isClientClosing returns true if the given error is a
Expand Down
10 changes: 5 additions & 5 deletions spanner/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ func (t *txReadOnly) ReadWithOptions(ctx context.Context, table string, keys Key
if err != nil {
if _, ok := t.getTransactionSelector().GetSelector().(*sppb.TransactionSelector_Begin); ok {
t.setTransactionID(nil)
return client, errInlineBeginTransactionFailed()
return client, toSpannerErrorDuringInlineBegin(err)
}
return client, err
}
Expand Down Expand Up @@ -305,7 +305,7 @@ func errMultipleRowsFound(table string, key Key, index string) error {

// errInlineBeginTransactionFailed returns error for read-write transaction to explicitly begin the transaction
func errInlineBeginTransactionFailed() error {
return spannerErrorf(codes.Internal, "failed inline begin transaction")
return spannerErrorf(codes.Internal, inlineBeginTransactionFailedMsg)
}

// ReadRow reads a single row from the database.
Expand Down Expand Up @@ -506,7 +506,7 @@ func (t *txReadOnly) query(ctx context.Context, statement Statement, options Que
if err != nil {
if _, ok := req.Transaction.GetSelector().(*sppb.TransactionSelector_Begin); ok {
t.setTransactionID(nil)
return client, errInlineBeginTransactionFailed()
return client, toSpannerErrorDuringInlineBegin(err)
}
return client, err
}
Expand Down Expand Up @@ -1094,7 +1094,7 @@ func (t *ReadWriteTransaction) update(ctx context.Context, stmt Statement, opts
if err != nil {
if hasInlineBeginTransaction {
t.setTransactionID(nil)
return 0, errInlineBeginTransactionFailed()
return 0, toSpannerErrorDuringInlineBegin(err)
}
return 0, ToSpannerError(err)
}
Expand Down Expand Up @@ -1197,7 +1197,7 @@ func (t *ReadWriteTransaction) batchUpdateWithOptions(ctx context.Context, stmts
if err != nil {
if hasInlineBeginTransaction {
t.setTransactionID(nil)
return nil, errInlineBeginTransactionFailed()
return nil, toSpannerErrorDuringInlineBegin(err)
}
return nil, ToSpannerError(err)
}
Expand Down
Loading