@@ -39,15 +39,23 @@ type Tx interface {
3939// fn is subject to the same restrictions as the fn passed to ExecuteTx.
4040func ExecuteInTx (ctx context.Context , tx Tx , fn func () error ) (err error ) {
4141 defer func () {
42- if err == nil {
42+ r := recover ()
43+
44+ if r == nil && err == nil {
4345 // Ignore commit errors. The tx has already been committed by RELEASE.
4446 _ = tx .Commit (ctx )
45- } else {
46- // We always need to execute a Rollback() so sql.DB releases the
47- // connection.
48- _ = tx .Rollback (ctx )
47+ return
48+ }
49+
50+ // We always need to execute a Rollback() so sql.DB releases the
51+ // connection.
52+ _ = tx .Rollback (ctx )
53+
54+ if r != nil {
55+ panic (r )
4956 }
5057 }()
58+
5159 // Specify that we intend to retry this txn in case of CockroachDB retryable
5260 // errors.
5361 if err = tx .Exec (ctx , "SAVEPOINT cockroach_restart" ); err != nil {
@@ -58,26 +66,27 @@ func ExecuteInTx(ctx context.Context, tx Tx, fn func() error) (err error) {
5866 const maxRetries = 50
5967 retryCount := 0
6068 for {
61- released := false
69+ releaseFailed := false
6270 err = fn ()
6371 if err == nil {
6472 // RELEASE acts like COMMIT in CockroachDB. We use it since it gives us an
6573 // opportunity to react to retryable errors, whereas tx.Commit() doesn't.
66- released = true
6774 if err = tx .Exec (ctx , "RELEASE SAVEPOINT cockroach_restart" ); err == nil {
6875 return nil
6976 }
77+ releaseFailed = true
7078 }
79+
7180 // We got an error; let's see if it's a retryable one and, if so, restart.
7281 if ! errIsRetryable (err ) {
73- if released {
82+ if releaseFailed {
7483 err = newAmbiguousCommitError (err )
7584 }
7685 return err
7786 }
7887
79- if retryErr := tx .Exec (ctx , "ROLLBACK TO SAVEPOINT cockroach_restart" ); retryErr != nil {
80- return newTxnRestartError (retryErr , err )
88+ if rollbackErr := tx .Exec (ctx , "ROLLBACK TO SAVEPOINT cockroach_restart" ); rollbackErr != nil {
89+ return newTxnRestartError (rollbackErr , err )
8190 }
8291
8392 retryCount ++
0 commit comments