@@ -1387,51 +1387,64 @@ func (t *writeOnlyTransaction) applyAtLeastOnce(ctx context.Context, ms ...*Muta
1387
1387
ts time.Time
1388
1388
sh * sessionHandle
1389
1389
)
1390
+ defer func () {
1391
+ if sh != nil {
1392
+ sh .recycle ()
1393
+ }
1394
+ }()
1390
1395
mPb , err := mutationsProto (ms )
1391
1396
if err != nil {
1392
1397
// Malformed mutation found, just return the error.
1393
1398
return ts , err
1394
1399
}
1395
1400
1396
- // Retry-loop for aborted transactions.
1397
- // TODO: Replace with generic retryer.
1398
- for {
1399
- if sh == nil || sh .getID () == "" || sh .getClient () == nil {
1400
- // No usable session for doing the commit, take one from pool.
1401
- sh , err = t .sp .take (ctx )
1402
- if err != nil {
1403
- // sessionPool.Take already retries for session
1404
- // creations/retrivals.
1405
- return ts , err
1401
+ // Make a retryer for Aborted and certain Internal errors.
1402
+ retryer := onCodes (DefaultRetryBackoff , codes .Aborted , codes .Internal )
1403
+ // Apply the mutation and retry if the commit is aborted.
1404
+ applyMutationWithRetry := func (ctx context.Context ) error {
1405
+ for {
1406
+ if sh == nil || sh .getID () == "" || sh .getClient () == nil {
1407
+ // No usable session for doing the commit, take one from pool.
1408
+ sh , err = t .sp .take (ctx )
1409
+ if err != nil {
1410
+ // sessionPool.Take already retries for session
1411
+ // creations/retrivals.
1412
+ return ToSpannerError (err )
1413
+ }
1406
1414
}
1407
- defer sh .recycle ()
1408
- }
1409
- res , err := sh .getClient ().Commit (contextWithOutgoingMetadata (ctx , sh .getMetadata ()), & sppb.CommitRequest {
1410
- Session : sh .getID (),
1411
- Transaction : & sppb.CommitRequest_SingleUseTransaction {
1412
- SingleUseTransaction : & sppb.TransactionOptions {
1413
- Mode : & sppb.TransactionOptions_ReadWrite_ {
1414
- ReadWrite : & sppb.TransactionOptions_ReadWrite {},
1415
+ res , err := sh .getClient ().Commit (contextWithOutgoingMetadata (ctx , sh .getMetadata ()), & sppb.CommitRequest {
1416
+ Session : sh .getID (),
1417
+ Transaction : & sppb.CommitRequest_SingleUseTransaction {
1418
+ SingleUseTransaction : & sppb.TransactionOptions {
1419
+ Mode : & sppb.TransactionOptions_ReadWrite_ {
1420
+ ReadWrite : & sppb.TransactionOptions_ReadWrite {},
1421
+ },
1415
1422
},
1416
1423
},
1417
- },
1418
- Mutations : mPb ,
1419
- RequestOptions : createRequestOptions (t .commitPriority , "" , t .transactionTag ),
1420
- })
1421
- if err != nil && ! isAbortedErr (err ) {
1422
- if isSessionNotFoundError (err ) {
1423
- // Discard the bad session.
1424
- sh .destroy ()
1424
+ Mutations : mPb ,
1425
+ RequestOptions : createRequestOptions (t .commitPriority , "" , t .transactionTag ),
1426
+ })
1427
+ if err != nil && ! isAbortedErr (err ) {
1428
+ if isSessionNotFoundError (err ) {
1429
+ // Discard the bad session.
1430
+ sh .destroy ()
1431
+ }
1432
+ return toSpannerErrorWithCommitInfo (err , true )
1433
+ } else if err == nil {
1434
+ if tstamp := res .GetCommitTimestamp (); tstamp != nil {
1435
+ ts = time .Unix (tstamp .Seconds , int64 (tstamp .Nanos ))
1436
+ }
1425
1437
}
1426
- return ts , toSpannerErrorWithCommitInfo (err , true )
1427
- } else if err == nil {
1428
- if tstamp := res .GetCommitTimestamp (); tstamp != nil {
1429
- ts = time .Unix (tstamp .Seconds , int64 (tstamp .Nanos ))
1438
+ delay , shouldRetry := retryer .Retry (err )
1439
+ if ! shouldRetry {
1440
+ return err
1441
+ }
1442
+ if err := gax .Sleep (ctx , delay ); err != nil {
1443
+ return err
1430
1444
}
1431
- break
1432
1445
}
1433
1446
}
1434
- return ts , ToSpannerError ( err )
1447
+ return ts , applyMutationWithRetry ( ctx )
1435
1448
}
1436
1449
1437
1450
// isAbortedErr returns true if the error indicates that an gRPC call is
0 commit comments