@@ -11,12 +11,15 @@ use derive_new::new;
1111use fail:: fail_point;
1212use futures:: prelude:: * ;
1313use log:: debug;
14+ use log:: error;
15+ use log:: info;
1416use log:: warn;
1517use tokio:: time:: Duration ;
1618
1719use crate :: backoff:: Backoff ;
1820use crate :: backoff:: DEFAULT_REGION_BACKOFF ;
1921use crate :: codec:: ApiV1TxnCodec ;
22+ use crate :: kv:: HexRepr ;
2023use crate :: pd:: PdClient ;
2124use crate :: pd:: PdRpcClient ;
2225use crate :: proto:: kvrpcpb;
@@ -1246,7 +1249,7 @@ impl<PdC: PdClient> Committer<PdC> {
12461249 let min_commit_ts = self . prewrite ( ) . await ?;
12471250
12481251 fail_point ! ( "after-prewrite" , |_| {
1249- Err ( Error :: StringError (
1252+ Err ( Error :: OtherError (
12501253 "failpoint: after-prewrite return error" . to_owned( ) ,
12511254 ) )
12521255 } ) ;
@@ -1260,7 +1263,7 @@ impl<PdC: PdClient> Committer<PdC> {
12601263 // FIXME: min_commit_ts == 0 => fallback to normal 2PC
12611264 min_commit_ts. unwrap ( )
12621265 } else {
1263- match self . commit_primary ( ) . await {
1266+ match self . commit_primary_with_retry ( ) . await {
12641267 Ok ( commit_ts) => commit_ts,
12651268 Err ( e) => {
12661269 return if self . undetermined {
@@ -1365,6 +1368,11 @@ impl<PdC: PdClient> Committer<PdC> {
13651368 . plan ( ) ;
13661369 plan. execute ( )
13671370 . inspect_err ( |e| {
1371+ debug ! (
1372+ "commit primary error: {:?}, start_ts: {}" ,
1373+ e,
1374+ self . start_version. version( )
1375+ ) ;
13681376 // We don't know whether the transaction is committed or not if we fail to receive
13691377 // the response. Then, we mark the transaction as undetermined and propagate the
13701378 // error to the user.
@@ -1377,6 +1385,48 @@ impl<PdC: PdClient> Committer<PdC> {
13771385 Ok ( commit_version)
13781386 }
13791387
1388+ async fn commit_primary_with_retry ( & mut self ) -> Result < Timestamp > {
1389+ loop {
1390+ match self . commit_primary ( ) . await {
1391+ Ok ( commit_version) => return Ok ( commit_version) ,
1392+ Err ( Error :: ExtractedErrors ( mut errors) ) => match errors. pop ( ) {
1393+ Some ( Error :: KeyError ( key_err) ) => {
1394+ if let Some ( expired) = key_err. commit_ts_expired {
1395+ // Ref: https://github.com/tikv/client-go/blob/tidb-8.5/txnkv/transaction/commit.go
1396+ info ! ( "2PC commit_ts rejected by TiKV, retry with a newer commit_ts, start_ts: {}" ,
1397+ self . start_version. version( ) ) ;
1398+
1399+ let primary_key = self . primary_key . as_ref ( ) . unwrap ( ) ;
1400+ if primary_key != expired. key . as_ref ( ) {
1401+ error ! ( "2PC commit_ts rejected by TiKV, but the key is not the primary key, start_ts: {}, key: {}, primary: {:?}" ,
1402+ self . start_version. version( ) , HexRepr ( & expired. key) , primary_key) ;
1403+ return Err ( Error :: OtherError ( "2PC commitTS rejected by TiKV, but the key is not the primary key" . to_string ( ) ) ) ;
1404+ }
1405+
1406+ // Do not retry for a txn which has a too large min_commit_ts.
1407+ // 3600000 << 18 = 943718400000
1408+ if expired
1409+ . min_commit_ts
1410+ . saturating_sub ( expired. attempted_commit_ts )
1411+ > 943718400000
1412+ {
1413+ let msg = format ! ( "2PC min_commit_ts is too large, we got min_commit_ts: {}, and attempted_commit_ts: {}" ,
1414+ expired. min_commit_ts, expired. attempted_commit_ts) ;
1415+ return Err ( Error :: OtherError ( msg) ) ;
1416+ }
1417+ continue ;
1418+ } else {
1419+ return Err ( Error :: KeyError ( key_err) ) ;
1420+ }
1421+ }
1422+ Some ( err) => return Err ( err) ,
1423+ None => unreachable ! ( ) ,
1424+ } ,
1425+ Err ( err) => return Err ( err) ,
1426+ }
1427+ }
1428+ }
1429+
13801430 async fn commit_secondary ( self , commit_version : Timestamp ) -> Result < ( ) > {
13811431 debug ! ( "committing secondary" ) ;
13821432 let mutations_len = self . mutations . len ( ) ;
@@ -1394,7 +1444,7 @@ impl<PdC: PdClient> Committer<PdC> {
13941444 let percent = percent. unwrap( ) . parse:: <usize >( ) . unwrap( ) ;
13951445 new_len = mutations_len * percent / 100 ;
13961446 if new_len == 0 {
1397- Err ( Error :: StringError (
1447+ Err ( Error :: OtherError (
13981448 "failpoint: before-commit-secondary return error" . to_owned( ) ,
13991449 ) )
14001450 } else {
0 commit comments