@@ -9,12 +9,12 @@ use std::time::Instant;
99use derive_new:: new;
1010use fail:: fail_point;
1111use futures:: prelude:: * ;
12- use log:: warn;
13- use log:: { debug, trace} ;
12+ use log:: { debug, error, info, trace, warn} ;
1413use tokio:: time:: Duration ;
1514
1615use crate :: backoff:: Backoff ;
1716use crate :: backoff:: DEFAULT_REGION_BACKOFF ;
17+ use crate :: kv:: HexRepr ;
1818use crate :: pd:: PdClient ;
1919use crate :: pd:: PdRpcClient ;
2020use crate :: proto:: kvrpcpb;
@@ -1293,7 +1293,7 @@ impl<PdC: PdClient> Committer<PdC> {
12931293 // FIXME: min_commit_ts == 0 => fallback to normal 2PC
12941294 min_commit_ts. unwrap ( )
12951295 } else {
1296- match self . commit_primary ( ) . await {
1296+ match self . commit_primary_with_retry ( ) . await {
12971297 Ok ( commit_ts) => commit_ts,
12981298 Err ( e) => {
12991299 return if self . undetermined {
@@ -1398,6 +1398,11 @@ impl<PdC: PdClient> Committer<PdC> {
13981398 . plan ( ) ;
13991399 plan. execute ( )
14001400 . inspect_err ( |e| {
1401+ debug ! (
1402+ "commit primary error: {:?}, start_ts: {}" ,
1403+ e,
1404+ self . start_version. version( )
1405+ ) ;
14011406 // We don't know whether the transaction is committed or not if we fail to receive
14021407 // the response. Then, we mark the transaction as undetermined and propagate the
14031408 // error to the user.
@@ -1410,6 +1415,48 @@ impl<PdC: PdClient> Committer<PdC> {
14101415 Ok ( commit_version)
14111416 }
14121417
1418+ async fn commit_primary_with_retry ( & mut self ) -> Result < Timestamp > {
1419+ loop {
1420+ match self . commit_primary ( ) . await {
1421+ Ok ( commit_version) => return Ok ( commit_version) ,
1422+ Err ( Error :: ExtractedErrors ( mut errors) ) => match errors. pop ( ) {
1423+ Some ( Error :: KeyError ( key_err) ) => {
1424+ if let Some ( expired) = key_err. commit_ts_expired {
1425+ // Ref: https://github.com/tikv/client-go/blob/tidb-8.5/txnkv/transaction/commit.go
1426+ info ! ( "2PC commit_ts rejected by TiKV, retry with a newer commit_ts, start_ts: {}" ,
1427+ self . start_version. version( ) ) ;
1428+
1429+ let primary_key = self . primary_key . as_ref ( ) . unwrap ( ) ;
1430+ if primary_key != expired. key . as_ref ( ) {
1431+ error ! ( "2PC commit_ts rejected by TiKV, but the key is not the primary key, start_ts: {}, key: {}, primary: {:?}" ,
1432+ self . start_version. version( ) , HexRepr ( & expired. key) , primary_key) ;
1433+ return Err ( Error :: StringError ( "2PC commitTS rejected by TiKV, but the key is not the primary key" . to_string ( ) ) ) ;
1434+ }
1435+
1436+ // Do not retry for a txn which has a too large min_commit_ts.
1437+ // 3600000 << 18 = 943718400000
1438+ if expired
1439+ . min_commit_ts
1440+ . saturating_sub ( expired. attempted_commit_ts )
1441+ > 943718400000
1442+ {
1443+ let msg = format ! ( "2PC min_commit_ts is too large, we got min_commit_ts: {}, and attempted_commit_ts: {}" ,
1444+ expired. min_commit_ts, expired. attempted_commit_ts) ;
1445+ return Err ( Error :: StringError ( msg) ) ;
1446+ }
1447+ continue ;
1448+ } else {
1449+ return Err ( Error :: KeyError ( key_err) ) ;
1450+ }
1451+ }
1452+ Some ( err) => return Err ( err) ,
1453+ None => unreachable ! ( ) ,
1454+ } ,
1455+ Err ( err) => return Err ( err) ,
1456+ }
1457+ }
1458+ }
1459+
14131460 async fn commit_secondary ( self , commit_version : Timestamp ) -> Result < ( ) > {
14141461 debug ! ( "committing secondary" ) ;
14151462 let start_version = self . start_version . clone ( ) ;
0 commit comments