Documentation
¶
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrEmptyDatabase is returned when no database connection is set. ErrEmptyDatabase = errors.New("no database connection is set") )
Functions ¶
This section is empty.
Types ¶
type ConfigFunc ¶ added in v0.4.0
type ConfigFunc func(*Transaction)
A ConfigFunc function sets up a Transaction.
func DelayMethod ¶ added in v0.4.0
func DelayMethod(m retry.DelayMethod) ConfigFunc
DelayMethod decides how to delay between each tries. Default is retry.StandardDelay.
func RetryCount ¶ added in v0.4.0
func RetryCount(n int) ConfigFunc
RetryCount defines a transaction should be tried n times. If n is 0, it will be set as 1.
func RetryDelay ¶ added in v0.4.0
func RetryDelay(d time.Duration) ConfigFunc
RetryDelay is the amount of delay between each unsuccessful tries. Set DelayMethod for the method of delay duration.
type Pool ¶ added in v0.4.0
Pool is the contract for beginning a transaction with a pgxpool db connection.
type Transaction ¶ added in v0.4.0
type Transaction struct {
// contains filtered or unexported fields
}
Transaction is a concurrent-safe object that can retry a transaction on either a sql.DB or a pgxpool connection until it succeeds.
DB and PGX will try transaction functions one-by-one until all of them return nil, then commits the transaction. If any of the transactions return any error other than retry.StopError, it will retry the transaction until the retry count is exhausted. If a running function returns a retry.StopError, the transaction will be rolled-back and would stop retrying. Tryouts will be stopped when the passed contexts are cancelled.
If all attempts return errors, the last error is returned. If a retry.StopError is returned, transaction is rolled back and the Err inside the retry.StopError is returned. There will be delays between tries defined by the retry.DelayMethod and Delay duration.
Any panic in transactions will be wrapped in an error and will be counted as an error, either being retried or returned.
It's an error to invoke the methods without their respective connections are set.
func NewTransaction ¶ added in v0.4.0
func NewTransaction(conn interface{}, conf ...ConfigFunc) (*Transaction, error)
NewTransaction returns an error if conn is not a DB, Pool, or *sql.DB connection.
Example ¶
// This setup tries the transaction only once. dbtools.NewTransaction(&exampleConn{}) // This setup tries 100 times until succeeds. The delay is set to 10ms and // it uses the retry.IncrementalDelay method, which means every time it // increments the delay between retries with a jitter to avoid thunder herd // problem. dbtools.NewTransaction(&exampleConn{}, dbtools.RetryCount(100), dbtools.RetryDelay(10*time.Millisecond), dbtools.DelayMethod(retry.IncrementalDelay), )
Output:
func (*Transaction) PGX ¶ added in v0.4.0
func (t *Transaction) PGX(ctx context.Context, transactions ...func(pgx.Tx) error) error
PGX returns an error if a pgxpool connection is not set.
Example ¶
tr, err := dbtools.NewTransaction(&exampleConn{}) if err != nil { panic(err) } err = tr.PGX(context.Background(), func(pgx.Tx) error { fmt.Println("Running first query.") return nil }, func(pgx.Tx) error { fmt.Println("Running second query.") return nil }) fmt.Printf("Transaction's error: %v", err)
Output: Running first query. Running second query. Transaction's error: <nil>
Example (Panics) ¶
tr, err := dbtools.NewTransaction(&exampleConn{}, dbtools.RetryCount(10)) if err != nil { panic(err) } calls := 0 err = tr.PGX(context.Background(), func(pgx.Tx) error { calls++ fmt.Printf("Call #%d.\n", calls) if calls < 5 { panic("We have a panic!") } fmt.Println("All done.") return nil }) fmt.Printf("Transaction's error: %v\n", err) fmt.Printf("Called %d times.\n", calls)
Output: Call #1. Call #2. Call #3. Call #4. Call #5. All done. Transaction's error: <nil> Called 5 times.
Example (Retries) ¶
tr, err := dbtools.NewTransaction(&exampleConn{}, dbtools.RetryCount(10)) if err != nil { panic(err) } called := false err = tr.PGX(context.Background(), func(pgx.Tx) error { fmt.Println("Running first query.") return nil }, func(pgx.Tx) error { if !called { called = true fmt.Println("Second query error.") return assert.AnError } fmt.Println("Running second query.") return nil }) fmt.Printf("Transaction's error: %v", err)
Output: Running first query. Second query error. Running first query. Running second query. Transaction's error: <nil>
Example (StopTrying) ¶
// This example shows how to stop trying when we know an error is not // recoverable. tr, err := dbtools.NewTransaction(&exampleConn{}, dbtools.RetryCount(100), dbtools.RetryDelay(time.Second), ) if err != nil { panic(err) } err = tr.PGX(context.Background(), func(pgx.Tx) error { fmt.Println("Running first query.") return nil }, func(pgx.Tx) error { fmt.Println("Running second query.") return &retry.StopError{Err: assert.AnError} }) fmt.Printf("Transaction returns my error: %t", strings.Contains(err.Error(), assert.AnError.Error()))
Output: Running first query. Running second query. Transaction returns my error: true
type Tx ¶ added in v0.4.0
type Tx interface { Commit() error Exec(query string, args ...interface{}) (sql.Result, error) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) Prepare(query string) (*sql.Stmt, error) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error) Query(query string, args ...interface{}) (*sql.Rows, error) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) QueryRow(query string, args ...interface{}) *sql.Row QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row Rollback() error Stmt(stmt *sql.Stmt) *sql.Stmt StmtContext(ctx context.Context, stmt *sql.Stmt) *sql.Stmt }
Tx is a transaction began with sql.DB.