Skip to content

Commit 9927a47

Browse files
authored
Merge pull request #1260 from ydb-platform/retry-with-result
Added `retry.DoWithResult` and `retry.DoTxWithResult`
2 parents eedd068 + 527bfcd commit 9927a47

File tree

6 files changed

+131
-62
lines changed

6 files changed

+131
-62
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
* Added experimental `retry.DoWithResult` and `retry.DoTxWithResult` helpers for retry lambda and return value from lambda
2+
13
## v3.72.0
24
* Excluded `Query()` method from interface `ydb.Connection`. Method `Query()` remains accessible from `ydb.Driver`
35

internal/query/scanner/named.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,25 @@ type (
1212
NamedScanner struct {
1313
data *data
1414
}
15-
NamedDestination struct {
15+
namedDestination struct {
1616
name string
1717
ref interface{}
1818
}
19+
NamedDestination interface {
20+
Name() string
21+
Ref() interface{}
22+
}
1923
)
2024

21-
func NamedRef(columnName string, destinationValueReference interface{}) (dst NamedDestination) {
25+
func (dst namedDestination) Name() string {
26+
return dst.name
27+
}
28+
29+
func (dst namedDestination) Ref() interface{} {
30+
return dst.ref
31+
}
32+
33+
func NamedRef(columnName string, destinationValueReference interface{}) (dst namedDestination) {
2234
if columnName == "" {
2335
panic("columnName must be not empty")
2436
}
@@ -40,11 +52,11 @@ func Named(data *data) NamedScanner {
4052

4153
func (s NamedScanner) ScanNamed(dst ...NamedDestination) (err error) {
4254
for i := range dst {
43-
v, err := s.data.seekByName(dst[i].name)
55+
v, err := s.data.seekByName(dst[i].Name())
4456
if err != nil {
4557
return xerrors.WithStackTrace(err)
4658
}
47-
if err = value.CastTo(v, dst[i].ref); err != nil {
59+
if err = value.CastTo(v, dst[i].Ref()); err != nil {
4860
return xerrors.WithStackTrace(err)
4961
}
5062
}

internal/query/scanner/named_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -618,25 +618,25 @@ func TestNamedRef(t *testing.T) {
618618
{
619619
name: "",
620620
ref: nil,
621-
dst: NamedDestination{},
621+
dst: namedDestination{},
622622
panic: true,
623623
},
624624
{
625625
name: "nil_ref",
626626
ref: nil,
627-
dst: NamedDestination{},
627+
dst: namedDestination{},
628628
panic: true,
629629
},
630630
{
631631
name: "not_ref",
632632
ref: 123,
633-
dst: NamedDestination{},
633+
dst: namedDestination{},
634634
panic: true,
635635
},
636636
{
637637
name: "int_ptr",
638638
ref: func(v int) *int { return &v }(123),
639-
dst: NamedDestination{
639+
dst: namedDestination{
640640
name: "int_ptr",
641641
ref: func(v int) *int { return &v }(123),
642642
},
@@ -649,7 +649,7 @@ func TestNamedRef(t *testing.T) {
649649

650650
return &vv
651651
}(123),
652-
dst: NamedDestination{
652+
dst: namedDestination{
653653
name: "int_dbl_ptr",
654654
ref: func(v int) **int {
655655
vv := &v

query/result.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,28 +17,31 @@ type (
1717
}
1818
ResultSet interface {
1919
Columns() []string
20-
ColumnTypes() []types.Type
20+
ColumnTypes() []Type
2121
NextRow(ctx context.Context) (Row, error)
2222
}
2323
Row interface {
2424
Scan(dst ...interface{}) error
25-
ScanNamed(dst ...scanner.NamedDestination) error
26-
ScanStruct(dst interface{}, opts ...scanner.ScanStructOption) error
25+
ScanNamed(dst ...NamedDestination) error
26+
ScanStruct(dst interface{}, opts ...ScanStructOption) error
2727
}
28+
Type = types.Type
29+
NamedDestination = scanner.NamedDestination
30+
ScanStructOption = scanner.ScanStructOption
2831
)
2932

30-
func Named(columnName string, destinationValueReference interface{}) (dst scanner.NamedDestination) {
33+
func Named(columnName string, destinationValueReference interface{}) (dst NamedDestination) {
3134
return scanner.NamedRef(columnName, destinationValueReference)
3235
}
3336

34-
func WithScanStructTagName(name string) scanner.ScanStructOption {
37+
func WithScanStructTagName(name string) ScanStructOption {
3538
return scanner.WithTagName(name)
3639
}
3740

38-
func WithScanStructAllowMissingColumnsFromSelect() scanner.ScanStructOption {
41+
func WithScanStructAllowMissingColumnsFromSelect() ScanStructOption {
3942
return scanner.WithAllowMissingColumnsFromSelect()
4043
}
4144

42-
func WithScanStructAllowMissingFieldsInStruct() scanner.ScanStructOption {
45+
func WithScanStructAllowMissingFieldsInStruct() ScanStructOption {
4346
return scanner.WithAllowMissingFieldsInStruct()
4447
}

retry/retry.go

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -257,10 +257,8 @@ 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) {
263-
_, err := RetryWithResult[struct{}](ctx, func(ctx context.Context) (*struct{}, error) {
261+
_, err := RetryWithResult[*struct{}](ctx, func(ctx context.Context) (*struct{}, error) {
264262
err := op(ctx)
265263
if err != nil {
266264
return nil, xerrors.WithStackTrace(err)
@@ -285,19 +283,22 @@ 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
292-
op func(context.Context) (*T, error), opts ...Option,
293-
) (v *T, finalErr error) {
294-
options := &retryOptions{
295-
call: stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/retry.RetryWithResult"),
296-
trace: &trace.Retry{},
297-
budget: budget.Limited(-1),
298-
fastBackoff: backoff.Fast,
299-
slowBackoff: backoff.Slow,
300-
}
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
290+
op func(context.Context) (T, error), opts ...Option,
291+
) (_ T, finalErr error) {
292+
var (
293+
zeroValue T
294+
options = &retryOptions{
295+
call: stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/3/retry.RetryWithResult"),
296+
trace: &trace.Retry{},
297+
budget: budget.Limited(-1),
298+
fastBackoff: backoff.Fast,
299+
slowBackoff: backoff.Slow,
300+
}
301+
)
301302
for _, opt := range opts {
302303
if opt != nil {
303304
opt.ApplyRetryOption(options)
@@ -332,13 +333,12 @@ func RetryWithResult[T any](ctx context.Context, //nolint:revive
332333
attempts++
333334
select {
334335
case <-ctx.Done():
335-
return nil, xerrors.WithStackTrace(
336+
return zeroValue, xerrors.WithStackTrace(
336337
fmt.Errorf("retry failed on attempt No.%d: %w", attempts, ctx.Err()),
337338
)
338339

339340
default:
340-
var err error
341-
v, err = opWithRecover(ctx, options, op)
341+
v, err := opWithRecover(ctx, options, op)
342342

343343
if err == nil {
344344
return v, nil
@@ -353,7 +353,7 @@ func RetryWithResult[T any](ctx context.Context, //nolint:revive
353353
code = m.StatusCode()
354354

355355
if !m.MustRetry(options.idempotent) {
356-
return nil, xerrors.WithStackTrace(
356+
return zeroValue, xerrors.WithStackTrace(
357357
fmt.Errorf("non-retryable error occurred on attempt No.%d (idempotent=%v): %w",
358358
attempts, options.idempotent, err),
359359
)
@@ -368,7 +368,7 @@ func RetryWithResult[T any](ctx context.Context, //nolint:revive
368368
case <-ctx.Done():
369369
t.Stop()
370370

371-
return nil, xerrors.WithStackTrace(
371+
return zeroValue, xerrors.WithStackTrace(
372372
xerrors.Join(
373373
fmt.Errorf("attempt No.%d: %w", attempts, ctx.Err()),
374374
err,
@@ -378,7 +378,7 @@ func RetryWithResult[T any](ctx context.Context, //nolint:revive
378378
t.Stop()
379379

380380
if acquireErr := options.budget.Acquire(ctx); acquireErr != nil {
381-
return nil, xerrors.WithStackTrace(
381+
return zeroValue, xerrors.WithStackTrace(
382382
xerrors.Join(
383383
fmt.Errorf("attempt No.%d: %w", attempts, budget.ErrNoQuota),
384384
acquireErr,
@@ -392,20 +392,26 @@ func RetryWithResult[T any](ctx context.Context, //nolint:revive
392392
}
393393

394394
func opWithRecover[T any](ctx context.Context,
395-
options *retryOptions, op func(context.Context) (*T, error),
396-
) (_ *T, err error) {
395+
options *retryOptions, op func(context.Context) (T, error),
396+
) (_ T, finalErr error) {
397+
var zeroValue T
397398
if options.panicCallback != nil {
398399
defer func() {
399400
if e := recover(); e != nil {
400401
options.panicCallback(e)
401-
err = xerrors.WithStackTrace(
402+
finalErr = xerrors.WithStackTrace(
402403
fmt.Errorf("panic recovered: %v", e),
403404
)
404405
}
405406
}()
406407
}
407408

408-
return op(ctx)
409+
v, err := op(ctx)
410+
if err != nil {
411+
return zeroValue, xerrors.WithStackTrace(err)
412+
}
413+
414+
return v, nil
409415
}
410416

411417
// Check returns retry mode for queryErr.

0 commit comments

Comments
 (0)