Skip to content
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
* Changed `retry.Do`/`retry.DoWithResult` and `retry.DoTx`/`retry.DoTxWithResult` option handling to unify Option usage and simplify implementation

## v3.113.6
* Removed experimental label from `retry.DoWithResult` and `retry.DoTxWithResult`

Expand Down
72 changes: 0 additions & 72 deletions retry/retry.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,6 @@ var _ Option = labelOption("")

type labelOption string

func (label labelOption) ApplyDoOption(opts *doOptions) {
opts.retryOptions = append(opts.retryOptions, WithLabel(string(label)))
}

func (label labelOption) ApplyDoTxOption(opts *doTxOptions) {
opts.retryOptions = append(opts.retryOptions, WithLabel(string(label)))
}

func (label labelOption) ApplyRetryOption(opts *retryOptions) {
opts.label = string(label)
}
Expand All @@ -62,14 +54,6 @@ type callOption struct {
call
}

func (call callOption) ApplyDoOption(opts *doOptions) {
opts.retryOptions = append(opts.retryOptions, withCaller(call))
}

func (call callOption) ApplyDoTxOption(opts *doTxOptions) {
opts.retryOptions = append(opts.retryOptions, withCaller(call))
}

func (call callOption) ApplyRetryOption(opts *retryOptions) {
opts.call = call
}
Expand All @@ -90,14 +74,6 @@ func (stackTraceOption) ApplyRetryOption(opts *retryOptions) {
opts.stackTrace = true
}

func (stackTraceOption) ApplyDoOption(opts *doOptions) {
opts.retryOptions = append(opts.retryOptions, WithStackTrace())
}

func (stackTraceOption) ApplyDoTxOption(opts *doTxOptions) {
opts.retryOptions = append(opts.retryOptions, WithStackTrace())
}

// WithStackTrace wraps errors with stacktrace from Retry call
func WithStackTrace() stackTraceOption {
return stackTraceOption{}
Expand All @@ -113,14 +89,6 @@ func (t traceOption) ApplyRetryOption(opts *retryOptions) {
opts.trace = opts.trace.Compose(t.t)
}

func (t traceOption) ApplyDoOption(opts *doOptions) {
opts.retryOptions = append(opts.retryOptions, WithTrace(t.t))
}

func (t traceOption) ApplyDoTxOption(opts *doTxOptions) {
opts.retryOptions = append(opts.retryOptions, WithTrace(t.t))
}

// WithTrace returns trace option
func WithTrace(t *trace.Retry) traceOption {
return traceOption{t: t}
Expand All @@ -136,14 +104,6 @@ func (b budgetOption) ApplyRetryOption(opts *retryOptions) {
opts.budget = b.b
}

func (b budgetOption) ApplyDoOption(opts *doOptions) {
opts.retryOptions = append(opts.retryOptions, WithBudget(b.b))
}

func (b budgetOption) ApplyDoTxOption(opts *doTxOptions) {
opts.retryOptions = append(opts.retryOptions, WithBudget(b.b))
}

// WithBudget returns budget option
//
// Experimental: https://github.com/ydb-platform/ydb-go-sdk/blob/master/VERSIONING.md#experimental
Expand All @@ -159,14 +119,6 @@ func (idempotent idempotentOption) ApplyRetryOption(opts *retryOptions) {
opts.idempotent = bool(idempotent)
}

func (idempotent idempotentOption) ApplyDoOption(opts *doOptions) {
opts.retryOptions = append(opts.retryOptions, WithIdempotent(bool(idempotent)))
}

func (idempotent idempotentOption) ApplyDoTxOption(opts *doTxOptions) {
opts.retryOptions = append(opts.retryOptions, WithIdempotent(bool(idempotent)))
}

// WithIdempotent applies idempotent flag to retry operation
func WithIdempotent(idempotent bool) idempotentOption {
return idempotentOption(idempotent)
Expand All @@ -184,14 +136,6 @@ func (o fastBackoffOption) ApplyRetryOption(opts *retryOptions) {
}
}

func (o fastBackoffOption) ApplyDoOption(opts *doOptions) {
opts.retryOptions = append(opts.retryOptions, WithFastBackoff(o.backoff))
}

func (o fastBackoffOption) ApplyDoTxOption(opts *doTxOptions) {
opts.retryOptions = append(opts.retryOptions, WithFastBackoff(o.backoff))
}

// WithFastBackoff replaces default fast backoff
func WithFastBackoff(b backoff.Backoff) fastBackoffOption {
return fastBackoffOption{backoff: b}
Expand All @@ -209,14 +153,6 @@ func (o slowBackoffOption) ApplyRetryOption(opts *retryOptions) {
}
}

func (o slowBackoffOption) ApplyDoOption(opts *doOptions) {
opts.retryOptions = append(opts.retryOptions, WithSlowBackoff(o.backoff))
}

func (o slowBackoffOption) ApplyDoTxOption(opts *doTxOptions) {
opts.retryOptions = append(opts.retryOptions, WithSlowBackoff(o.backoff))
}

// WithSlowBackoff replaces default slow backoff
func WithSlowBackoff(b backoff.Backoff) slowBackoffOption {
return slowBackoffOption{backoff: b}
Expand All @@ -232,14 +168,6 @@ func (o panicCallbackOption) ApplyRetryOption(opts *retryOptions) {
opts.panicCallback = o.callback
}

func (o panicCallbackOption) ApplyDoOption(opts *doOptions) {
opts.retryOptions = append(opts.retryOptions, WithPanicCallback(o.callback))
}

func (o panicCallbackOption) ApplyDoTxOption(opts *doTxOptions) {
opts.retryOptions = append(opts.retryOptions, WithPanicCallback(o.callback))
}

// WithPanicCallback returns panic callback option
// If not defined - panic would not intercept with driver
func WithPanicCallback(panicCallback func(e interface{})) panicCallbackOption {
Expand Down
127 changes: 42 additions & 85 deletions retry/sql.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,36 +14,8 @@ import (
"github.com/ydb-platform/ydb-go-sdk/v3/trace"
)

type doOptions struct {
retryOptions []Option
}

// doTxOption defines option for redefine default Retry behavior
type doOption interface {
ApplyDoOption(opts *doOptions)
}

var (
_ doOption = doRetryOptionsOption(nil)
_ doOption = labelOption("")
)

type doRetryOptionsOption []Option

func (retryOptions doRetryOptionsOption) ApplyDoOption(opts *doOptions) {
opts.retryOptions = append(opts.retryOptions, retryOptions...)
}

// WithDoRetryOptions specified retry options
// Deprecated: use explicit options instead.
// Will be removed after Oct 2024.
// Read about versioning policy: https://github.com/ydb-platform/ydb-go-sdk/blob/master/VERSIONING.md#deprecated
func WithDoRetryOptions(opts ...Option) doRetryOptionsOption {
return opts
}

// Do is a retryer of database/sql conn with fallbacks on errors
func Do(ctx context.Context, db *sql.DB, op func(ctx context.Context, cc *sql.Conn) error, opts ...doOption) error {
func Do(ctx context.Context, db *sql.DB, op func(ctx context.Context, cc *sql.Conn) error, opts ...Option) error {
_, err := DoWithResult(ctx, db, func(ctx context.Context, cc *sql.Conn) (*struct{}, error) {
err := op(ctx, cc)
if err != nil {
Expand All @@ -62,29 +34,25 @@ func Do(ctx context.Context, db *sql.DB, op func(ctx context.Context, cc *sql.Co
// DoWithResult is a retryer of database/sql conn with fallbacks on errors
func DoWithResult[T any](ctx context.Context, db *sql.DB,
op func(ctx context.Context, cc *sql.Conn) (T, error),
opts ...doOption,
opts ...Option,
) (T, error) {
var (
zeroValue T
options = doOptions{
retryOptions: []Option{
withCaller(stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/v3/retry.DoWithResult")),
},
options = []Option{
withCaller(stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/v3/retry.DoWithResult")),
}
attempts = 0
)
if tracer, has := db.Driver().(interface {
TraceRetry() *trace.Retry
}); has {
options.retryOptions = append(options.retryOptions, nil)
copy(options.retryOptions[1:], options.retryOptions)
options.retryOptions[0] = WithTrace(tracer.TraceRetry())
}
for _, opt := range opts {
if opt != nil {
opt.ApplyDoOption(&options)
}
options = append(options, nil)
copy(options[1:], options)
options[0] = WithTrace(tracer.TraceRetry())
}

options = append(options, opts...)

v, err := RetryWithResult(ctx, func(ctx context.Context) (_ T, finalErr error) {
attempts++
cc, err := db.Conn(ctx)
Expand All @@ -106,7 +74,7 @@ func DoWithResult[T any](ctx context.Context, db *sql.DB,
}

return v, nil
}, options.retryOptions...)
}, options...)
if err != nil {
return zeroValue, xerrors.WithStackTrace(
fmt.Errorf("operation failed with %d attempts: %w", attempts, err),
Expand All @@ -116,40 +84,27 @@ func DoWithResult[T any](ctx context.Context, db *sql.DB,
return v, nil
}

type doTxOptions struct {
txOptions *sql.TxOptions
retryOptions []Option
}

// doTxOption defines option for redefine default Retry behavior
type doTxOption interface {
ApplyDoTxOption(o *doTxOptions)
ApplyDoTxOption(txOptions *sql.TxOptions)
}

var _ doTxOption = doTxRetryOptionsOption(nil)

type doTxRetryOptionsOption []Option

func (doTxRetryOptions doTxRetryOptionsOption) ApplyDoTxOption(o *doTxOptions) {
o.retryOptions = append(o.retryOptions, doTxRetryOptions...)
}

// WithDoTxRetryOptions specified retry options
// Deprecated: use explicit options instead.
// Will be removed after Oct 2024.
// Read about versioning policy: https://github.com/ydb-platform/ydb-go-sdk/blob/master/VERSIONING.md#deprecated
func WithDoTxRetryOptions(opts ...Option) doTxRetryOptionsOption {
return opts
}

var _ doTxOption = txOptionsOption{}
var (
_ Option = txOptionsOption{}
_ doTxOption = txOptionsOption{}
)

type txOptionsOption struct {
txOptions *sql.TxOptions
}

func (txOptions txOptionsOption) ApplyDoTxOption(o *doTxOptions) {
o.txOptions = txOptions.txOptions
func (t txOptionsOption) ApplyRetryOption(_ *retryOptions) {
}

func (t txOptionsOption) ApplyDoTxOption(txOptions *sql.TxOptions) {
if t.txOptions != nil {
*txOptions = *t.txOptions
}
}

// WithTxOptions specified transaction options
Expand All @@ -160,7 +115,7 @@ func WithTxOptions(txOptions *sql.TxOptions) txOptionsOption {
}

// DoTx is a retryer of database/sql transactions with fallbacks on errors
func DoTx(ctx context.Context, db *sql.DB, op func(context.Context, *sql.Tx) error, opts ...doTxOption) error {
func DoTx(ctx context.Context, db *sql.DB, op func(context.Context, *sql.Tx) error, opts ...Option) error {
_, err := DoTxWithResult(ctx, db, func(ctx context.Context, tx *sql.Tx) (*struct{}, error) {
err := op(ctx, tx)
if err != nil {
Expand All @@ -179,38 +134,40 @@ func DoTx(ctx context.Context, db *sql.DB, op func(context.Context, *sql.Tx) err
// DoTxWithResult is a retryer of database/sql transactions with fallbacks on errors
func DoTxWithResult[T any](ctx context.Context, db *sql.DB,
op func(context.Context, *sql.Tx) (T, error),
opts ...doTxOption,
opts ...Option,
) (T, error) {
var (
zeroValue T
options = doTxOptions{
retryOptions: []Option{
withCaller(stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/v3/retry.DoTxWithResult")),
},
txOptions: &sql.TxOptions{
Isolation: sql.LevelDefault,
ReadOnly: false,
},
options = []Option{
withCaller(stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/v3/retry.DoTxWithResult")),
}
txOptions = &sql.TxOptions{
Isolation: sql.LevelDefault,
ReadOnly: false,
}
attempts = 0
)
if d, has := db.Driver().(interface {
TraceRetry() *trace.Retry
RetryBudget() budget.Budget
}); has {
options.retryOptions = append(options.retryOptions, nil, nil)
copy(options.retryOptions[2:], options.retryOptions)
options.retryOptions[0] = WithTrace(d.TraceRetry())
options.retryOptions[1] = WithBudget(d.RetryBudget())
options = append(options, nil, nil)
copy(options[2:], options)
options[0] = WithTrace(d.TraceRetry())
options[1] = WithBudget(d.RetryBudget())
}
for _, opt := range opts {
if opt != nil {
opt.ApplyDoTxOption(&options)
if txOpt, ok := opt.(doTxOption); ok {
txOpt.ApplyDoTxOption(txOptions)
} else {
options = append(options, opt)
}
}
}
v, err := RetryWithResult(ctx, func(ctx context.Context) (_ T, finalErr error) {
attempts++
tx, err := db.BeginTx(ctx, options.txOptions)
tx, err := db.BeginTx(ctx, txOptions)
if err != nil {
return zeroValue, xerrors.WithStackTrace(err)
}
Expand All @@ -226,7 +183,7 @@ func DoTxWithResult[T any](ctx context.Context, db *sql.DB,
}

return v, nil
}, options.retryOptions...)
}, options...)
if err != nil {
return zeroValue, xerrors.WithStackTrace(
fmt.Errorf("tx operation failed with %d attempts: %w", attempts, err),
Expand Down
Loading