Skip to content

Commit c701c80

Browse files
committed
* Added experimental retry.DoWithResult and retry.DoTxWithResult helpers for retry lambda and return value from lambda
1 parent 24a46c5 commit c701c80

File tree

3 files changed

+71
-26
lines changed

3 files changed

+71
-26
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
* Added experimental `retry.DoWithResult` and `retry.DoTxWithResult` helpers for retry lambda and return value from lambda
12
* Excluded `Query()` method from interface `ydb.Connection`. Method `Query()` remains accessible from `ydb.Driver`
23

34
## v3.71.0

retry/retry.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -257,8 +257,6 @@ func WithPanicCallback(panicCallback func(e interface{})) panicCallbackOption {
257257
// Warning: if context without deadline or cancellation func was passed, Retry will work infinitely.
258258
//
259259
// # If you need to retry your op func on some logic errors - you must return RetryableError() from retryOperation
260-
//
261-
// Experimental: https://github.com/ydb-platform/ydb-go-sdk/blob/master/VERSIONING.md#experimental
262260
func Retry(ctx context.Context, op retryOperation, opts ...Option) (finalErr error) {
263261
_, err := RetryWithResult[*struct{}](ctx, func(ctx context.Context) (*struct{}, error) {
264262
err := op(ctx)
@@ -285,10 +283,10 @@ func Retry(ctx context.Context, op retryOperation, opts ...Option) (finalErr err
285283
//
286284
// Warning: if context without deadline or cancellation func was passed, RetryWithResult will work infinitely.
287285
//
288-
// If you need to retry your op func on some logic errors - you must return RetryableError() from retryOperation
286+
// # If you need to retry your op func on some logic errors - you must return RetryableError() from retryOperation
289287
//
290-
//nolint:funlen
291-
func RetryWithResult[T any](ctx context.Context, //nolint:revive
288+
// Experimental: https://github.com/ydb-platform/ydb-go-sdk/blob/master/VERSIONING.md#experimental
289+
func RetryWithResult[T any](ctx context.Context, //nolint:revive,funlen
292290
op func(context.Context) (T, error), opts ...Option,
293291
) (_ T, finalErr error) {
294292
var (

retry/sql.go

Lines changed: 67 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,33 @@ func WithDoRetryOptions(opts ...Option) doRetryOptionsOption {
4242

4343
// Do is a retryer of database/sql Conn with fallbacks on errors
4444
func Do(ctx context.Context, db *sql.DB, op func(ctx context.Context, cc *sql.Conn) error, opts ...doOption) error {
45+
_, err := DoWithResult(ctx, db, func(ctx context.Context, cc *sql.Conn) (*struct{}, error) {
46+
err := op(ctx, cc)
47+
if err != nil {
48+
return nil, xerrors.WithStackTrace(err)
49+
}
50+
51+
return nil, nil //nolint:nilnil
52+
}, opts...)
53+
if err != nil {
54+
return xerrors.WithStackTrace(err)
55+
}
56+
57+
return nil
58+
}
59+
60+
// DoWithResult is a retryer of database/sql Conn with fallbacks on errors
61+
//
62+
// Experimental: https://github.com/ydb-platform/ydb-go-sdk/blob/master/VERSIONING.md#experimental
63+
func DoWithResult[T any](ctx context.Context, db *sql.DB,
64+
op func(ctx context.Context, cc *sql.Conn) (T, error),
65+
opts ...doOption,
66+
) (T, error) {
4567
var (
46-
options = doOptions{
68+
zeroValue T
69+
options = doOptions{
4770
retryOptions: []Option{
48-
withCaller(stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/retry.Do")),
71+
withCaller(stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/retry.DoWithResult")),
4972
},
5073
}
5174
attempts = 0
@@ -62,28 +85,29 @@ func Do(ctx context.Context, db *sql.DB, op func(ctx context.Context, cc *sql.Co
6285
opt.ApplyDoOption(&options)
6386
}
6487
}
65-
err := Retry(ctx, func(ctx context.Context) error {
88+
v, err := RetryWithResult(ctx, func(ctx context.Context) (T, error) {
6689
attempts++
6790
cc, err := db.Conn(ctx)
6891
if err != nil {
69-
return unwrapErrBadConn(xerrors.WithStackTrace(err))
92+
return zeroValue, unwrapErrBadConn(xerrors.WithStackTrace(err))
7093
}
7194
defer func() {
7295
_ = cc.Close()
7396
}()
74-
if err = op(xcontext.MarkRetryCall(ctx), cc); err != nil {
75-
return unwrapErrBadConn(xerrors.WithStackTrace(err))
97+
v, err := op(xcontext.MarkRetryCall(ctx), cc)
98+
if err != nil {
99+
return zeroValue, unwrapErrBadConn(xerrors.WithStackTrace(err))
76100
}
77101

78-
return nil
102+
return v, nil
79103
}, options.retryOptions...)
80104
if err != nil {
81-
return xerrors.WithStackTrace(
105+
return zeroValue, xerrors.WithStackTrace(
82106
fmt.Errorf("operation failed with %d attempts: %w", attempts, err),
83107
)
84108
}
85109

86-
return nil
110+
return v, nil
87111
}
88112

89113
type doTxOptions struct {
@@ -130,13 +154,34 @@ func WithTxOptions(txOptions *sql.TxOptions) txOptionsOption {
130154
}
131155

132156
// DoTx is a retryer of database/sql transactions with fallbacks on errors
133-
//
134-
//nolint:funlen
135157
func DoTx(ctx context.Context, db *sql.DB, op func(context.Context, *sql.Tx) error, opts ...doTxOption) error {
158+
_, err := DoTxWithResult(ctx, db, func(ctx context.Context, tx *sql.Tx) (*struct{}, error) {
159+
err := op(ctx, tx)
160+
if err != nil {
161+
return nil, xerrors.WithStackTrace(err)
162+
}
163+
164+
return nil, nil //nolint:nilnil
165+
}, opts...)
166+
if err != nil {
167+
return xerrors.WithStackTrace(err)
168+
}
169+
170+
return nil
171+
}
172+
173+
// DoTxWithResult is a retryer of database/sql transactions with fallbacks on errors
174+
//
175+
// Experimental: https://github.com/ydb-platform/ydb-go-sdk/blob/master/VERSIONING.md#experimental
176+
func DoTxWithResult[T any](ctx context.Context, db *sql.DB, //nolint:funlen
177+
op func(context.Context, *sql.Tx) (T, error),
178+
opts ...doTxOption,
179+
) (T, error) {
136180
var (
137-
options = doTxOptions{
181+
zeroValue T
182+
options = doTxOptions{
138183
retryOptions: []Option{
139-
withCaller(stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/retry.DoTx")),
184+
withCaller(stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/retry.DoTxWithResult")),
140185
},
141186
txOptions: &sql.TxOptions{
142187
Isolation: sql.LevelDefault,
@@ -159,11 +204,11 @@ func DoTx(ctx context.Context, db *sql.DB, op func(context.Context, *sql.Tx) err
159204
opt.ApplyDoTxOption(&options)
160205
}
161206
}
162-
err := Retry(ctx, func(ctx context.Context) (finalErr error) {
207+
v, err := RetryWithResult(ctx, func(ctx context.Context) (_ T, finalErr error) {
163208
attempts++
164209
tx, err := db.BeginTx(ctx, options.txOptions)
165210
if err != nil {
166-
return unwrapErrBadConn(xerrors.WithStackTrace(err))
211+
return zeroValue, unwrapErrBadConn(xerrors.WithStackTrace(err))
167212
}
168213
defer func() {
169214
if finalErr == nil {
@@ -178,20 +223,21 @@ func DoTx(ctx context.Context, db *sql.DB, op func(context.Context, *sql.Tx) err
178223
xerrors.WithStackTrace(fmt.Errorf("rollback failed: %w", errRollback)),
179224
)
180225
}()
181-
if err = op(xcontext.MarkRetryCall(ctx), tx); err != nil {
182-
return unwrapErrBadConn(xerrors.WithStackTrace(err))
226+
v, err := op(xcontext.MarkRetryCall(ctx), tx)
227+
if err != nil {
228+
return zeroValue, unwrapErrBadConn(xerrors.WithStackTrace(err))
183229
}
184230
if err = tx.Commit(); err != nil {
185-
return unwrapErrBadConn(xerrors.WithStackTrace(err))
231+
return zeroValue, unwrapErrBadConn(xerrors.WithStackTrace(err))
186232
}
187233

188-
return nil
234+
return v, nil
189235
}, options.retryOptions...)
190236
if err != nil {
191-
return xerrors.WithStackTrace(
237+
return zeroValue, xerrors.WithStackTrace(
192238
fmt.Errorf("tx operation failed with %d attempts: %w", attempts, err),
193239
)
194240
}
195241

196-
return nil
242+
return v, nil
197243
}

0 commit comments

Comments
 (0)