@@ -293,7 +293,8 @@ func (store *MVCCStore) pessimisticLockInner(reqCtx *requestCtx, req *kvrpcpb.Pe
293
293
}
294
294
if ! dup {
295
295
for i , m := range mutations {
296
- lock , lockedWithConflictTS , err1 := store .buildPessimisticLock (m , items [i ], req )
296
+ latestExtraMeta := store .getLatestExtraMetaForKey (reqCtx , m )
297
+ lock , lockedWithConflictTS , err1 := store .buildPessimisticLock (m , items [i ], latestExtraMeta , req )
297
298
lockedWithConflictTSList = append (lockedWithConflictTSList , lockedWithConflictTS )
298
299
if err1 != nil {
299
300
return nil , err1
@@ -666,15 +667,19 @@ func (store *MVCCStore) handleCheckPessimisticErr(startTS uint64, err error, isF
666
667
667
668
// buildPessimisticLock builds the lock according to the request and the current state of the key.
668
669
// Returns the built lock, and the LockedWithConflictTS (if any, otherwise 0).
669
- func (store * MVCCStore ) buildPessimisticLock (m * kvrpcpb.Mutation , item * badger.Item ,
670
+ func (store * MVCCStore ) buildPessimisticLock (m * kvrpcpb.Mutation , item * badger.Item , latestExtraMeta mvcc. DBUserMeta ,
670
671
req * kvrpcpb.PessimisticLockRequest ) (* mvcc.Lock , uint64 , error ) {
671
672
var lockedWithConflictTS uint64 = 0
672
673
673
674
var writeConflictError error
674
675
675
676
if item != nil {
676
- userMeta := mvcc .DBUserMeta (item .UserMeta ())
677
677
if ! req .Force {
678
+ userMeta := mvcc .DBUserMeta (item .UserMeta ())
679
+ if latestExtraMeta != nil && latestExtraMeta .CommitTS () > userMeta .CommitTS () {
680
+ userMeta = latestExtraMeta
681
+ }
682
+
678
683
if userMeta .CommitTS () > req .ForUpdateTs {
679
684
writeConflictError = & kverrors.ErrConflict {
680
685
StartTS : req .StartVersion ,
@@ -734,6 +739,31 @@ func (store *MVCCStore) buildPessimisticLock(m *kvrpcpb.Mutation, item *badger.I
734
739
return lock , lockedWithConflictTS , nil
735
740
}
736
741
742
+ // getLatestExtraMetaForKey returns the userMeta of the extra txn status key with the biggest commit ts.
743
+ // It's used to assert whether a key has been written / locked by another transaction in the fair locking mechanism.
744
+ // Theoretically, the rollback record should be ignored. But we have no way to check the rollback record in the
745
+ // unistore. Returning record with a bigger commit ts may cause extra retry, but it's safe.
746
+ func (store * MVCCStore ) getLatestExtraMetaForKey (reqCtx * requestCtx , m * kvrpcpb.Mutation ) mvcc.DBUserMeta {
747
+ it := reqCtx .getDBReader ().GetExtraIter ()
748
+ rbStartKey := mvcc .EncodeExtraTxnStatusKey (m .Key , math .MaxUint64 )
749
+ rbEndKey := mvcc .EncodeExtraTxnStatusKey (m .Key , 0 )
750
+
751
+ for it .Seek (rbStartKey ); it .Valid (); it .Next () {
752
+ item := it .Item ()
753
+ if len (rbEndKey ) != 0 && bytes .Compare (item .Key (), rbEndKey ) > 0 {
754
+ break
755
+ }
756
+ key := item .Key ()
757
+ if len (key ) == 0 || (key [0 ] != tableExtraPrefix && key [0 ] != metaExtraPrefix ) {
758
+ continue
759
+ }
760
+
761
+ meta := mvcc .DBUserMeta (item .UserMeta ())
762
+ return meta
763
+ }
764
+ return nil
765
+ }
766
+
737
767
// Prewrite implements the MVCCStore interface.
738
768
func (store * MVCCStore ) Prewrite (reqCtx * requestCtx , req * kvrpcpb.PrewriteRequest ) error {
739
769
mutations := sortPrewrite (req )
0 commit comments