@@ -42,10 +42,33 @@ func WithDoRetryOptions(opts ...Option) doRetryOptionsOption {
4242
4343// Do is a retryer of database/sql Conn with fallbacks on errors
4444func 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
89113type 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
135157func 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