Documentation ¶
Overview ¶
Package crdb provides helpers for using CockroachDB in client applications.
Index ¶
- func Execute(fn func() error) (err error)
- func ExecuteInTx(ctx context.Context, tx Tx, fn func() error) (err error)
- func ExecuteTx(ctx context.Context, db *sql.DB, opts *sql.TxOptions, fn func(*sql.Tx) error) error
- func ExecuteTxGenericTest(ctx context.Context, framework WriteSkewTest) error
- type AmbiguousCommitError
- type MaxRetriesExceededError
- type Tx
- type TxnRestartError
- type WriteSkewTest
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Execute ¶
Execute runs fn and retries it as needed. It is used to add retry handling to the execution of a single statement. If a multi-statement transaction is being run, use ExecuteTx instead.
Retry handling for individual statements (implicit transactions) is usually performed automatically on the CockroachDB SQL gateway. As such, use of this function is generally not necessary. The exception to this rule is that automatic retries for individual statements are disabled once CockroachDB begins streaming results for the statements back to the client. By default, result streaming does not begin until the size of the result being produced for the client, including protocol overhead, exceeds 16KiB. As long as the results of a single statement or batch of statements are known to stay clear of this limit, the client does not need to worry about retries and should not need to use this function.
For more information about automatic transaction retries in CockroachDB, see https://cockroachlabs.com/docs/stable/transactions.html#automatic-retries.
NOTE: the supplied fn closure should not have external side effects beyond changes to the database.
fn must take care when wrapping errors returned from the database driver with additional context. For example, if the SELECT statement fails in the following snippet, the original retryable error will be masked by the call to fmt.Errorf, and the transaction will not be automatically retried.
crdb.Execute(func () error { rows, err := db.QueryContext(ctx, "SELECT ...") if err != nil { return fmt.Errorf("scanning row: %s", err) } defer rows.Close() for rows.Next() { // ... } if err := rows.Err(); err != nil { return fmt.Errorf("scanning row: %s", err) } return nil })
Instead, add context by returning an error that implements either: - a `Cause() error` method, in the manner of github.com/pkg/errors, or - an `Unwrap() error` method, in the manner of the Go 1.13 standard library.
To achieve this, you can implement your own error type, or use `errors.Wrap()` from github.com/pkg/errors or github.com/cockroachdb/errors or a similar package, or use go 1.13's special `%w` formatter with fmt.Errorf(), for example fmt.Errorf("scanning row: %w", err).
import "github.com/pkg/errors" crdb.Execute(func () error { rows, err := db.QueryContext(ctx, "SELECT ...") if err != nil { return errors.Wrap(err, "scanning row") } defer rows.Close() for rows.Next() { // ... } if err := rows.Err(); err != nil { return errors.Wrap(err, "scanning row") } return nil })
func ExecuteInTx ¶
ExecuteInTx runs fn inside tx. This method is primarily intended for internal use. See other packages for higher-level, framework-specific ExecuteTx() functions.
*WARNING*: It is assumed that no statements have been executed on the supplied Tx. ExecuteInTx will only retry statements that are performed within the supplied closure (fn). Any statements performed on the tx before ExecuteInTx is invoked will *not* be re-run if the transaction needs to be retried.
fn is subject to the same restrictions as the fn passed to ExecuteTx.
func ExecuteTx ¶
ExecuteTx runs fn inside a transaction and retries it as needed. On non-retryable failures, the transaction is aborted and rolled back; on success, the transaction is committed.
There are cases where the state of a transaction is inherently ambiguous: if we err on RELEASE with a communication error it's unclear if the transaction has been committed or not (similar to erroring on COMMIT in other databases). In that case, we return AmbiguousCommitError.
There are cases when restarting a transaction fails: we err on ROLLBACK to the SAVEPOINT. In that case, we return a TxnRestartError.
For more information about CockroachDB's transaction model, see https://cockroachlabs.com/docs/stable/transactions.html.
NOTE: the supplied fn closure should not have external side effects beyond changes to the database.
fn must take care when wrapping errors returned from the database driver with additional context. For example, if the UPDATE statement fails in the following snippet, the original retryable error will be masked by the call to fmt.Errorf, and the transaction will not be automatically retried.
crdb.ExecuteTx(ctx, db, txopts, func (tx *sql.Tx) error { if err := tx.ExecContext(ctx, "UPDATE..."); err != nil { return fmt.Errorf("updating record: %s", err) } return nil })
Instead, add context by returning an error that implements either: - a `Cause() error` method, in the manner of github.com/pkg/errors, or - an `Unwrap() error` method, in the manner of the Go 1.13 standard library.
To achieve this, you can implement your own error type, or use `errors.Wrap()` from github.com/pkg/errors or github.com/cockroachdb/errors or a similar package, or use go 1.13's special `%w` formatter with fmt.Errorf(), for example fmt.Errorf("scanning row: %w", err).
import "github.com/pkg/errors" crdb.ExecuteTx(ctx, db, txopts, func (tx *sql.Tx) error { if err := tx.ExecContext(ctx, "UPDATE..."); err != nil { return errors.Wrap(err, "updating record") } return nil })
func ExecuteTxGenericTest ¶
func ExecuteTxGenericTest(ctx context.Context, framework WriteSkewTest) error
ExecuteTxGenericTest represents the structure of a test for the ExecuteTx function. The actual database operations are abstracted by framework; the idea is that tests for different frameworks implement that interface and then invoke this test.
The test interleaves two transactions such that one of them will require a restart because of write skew.
Types ¶
type AmbiguousCommitError ¶
type AmbiguousCommitError struct {
// contains filtered or unexported fields
}
AmbiguousCommitError represents an error that left a transaction in an ambiguous state: unclear if it committed or not.
func (*AmbiguousCommitError) Cause ¶
func (e *AmbiguousCommitError) Cause() error
Cause implements the pkg/errors causer interface.
type MaxRetriesExceededError ¶ added in v2.0.8
type MaxRetriesExceededError struct {
// contains filtered or unexported fields
}
MaxRetriesExceededError represents an error caused by retying the transaction too many times, without it ever succeeding.
func (*MaxRetriesExceededError) Cause ¶ added in v2.0.8
func (e *MaxRetriesExceededError) Cause() error
Cause implements the pkg/errors causer interface.
type Tx ¶
type Tx interface { Exec(context.Context, string, ...interface{}) error Commit(context.Context) error Rollback(context.Context) error }
Tx abstracts the operations needed by ExecuteInTx so that different frameworks (e.g. go's sql package, pgx, gorm) can be used with ExecuteInTx.
type TxnRestartError ¶
type TxnRestartError struct {
// contains filtered or unexported fields
}
TxnRestartError represents an error when restarting a transaction. `cause` is the error from restarting the txn and `retryCause` is the original error which triggered the restart.
func (*TxnRestartError) Cause ¶
func (e *TxnRestartError) Cause() error
Cause implements the pkg/errors causer interface.
func (*TxnRestartError) Error ¶
func (e *TxnRestartError) Error() string
Error implements the error interface.
func (*TxnRestartError) RetryCause ¶
func (e *TxnRestartError) RetryCause() error
RetryCause returns the error that caused the transaction to be restarted.
type WriteSkewTest ¶
type WriteSkewTest interface { Init(context.Context) error ExecuteTx(ctx context.Context, fn func(tx interface{}) error) error GetBalances(ctx context.Context, tx interface{}) (bal1, bal2 int, err error) UpdateBalance(ctx context.Context, tx interface{}, acct, delta int) error }
WriteSkewTest abstracts the operations that needs to be performed by a particular framework for the purposes of TestExecuteTx. This allows the test to be written once and run for any framework supported by this library.