// CheckTxnStatus checks the primary lock of a transaction to decide its status.
// The return values are (ttl, commitTS, err):
// If the transaction is active, this function returns the ttl of the lock;
// If the transaction is committed, this function returns the commitTS;
// If the transaction is rollbacked, this function returns (0, 0, nil)
// Note that CheckTxnStatus may also push forward the `minCommitTS` of the
// transaction, so it's not simply a read-only operation.
//
// primaryKey + lockTS together could locate the primary lock.
// callerStartTS is the start ts of reader transaction.
// currentTS is the current ts, but it may be inaccurate. Just use it to check TTL.
CheckTxnStatus checks the primary lock of a transaction to decide its status.
The return values are (ttl, commitTS, err):
If the transaction is active, this function returns the ttl of the lock;
If the transaction is committed, this function returns the commitTS;
If the transaction is rollbacked, this function returns (0, 0, nil)
Note that CheckTxnStatus may also push forward the `minCommitTS` of the
transaction, so it's not simply a read-only operation.

primaryKey + lockTS together could locate the primary lock.
callerStartTS is the start ts of reader transaction.
currentTS is the current ts, but it may be inaccurate. Just use it to check TTL.
// CheckTxnStatus checks the primary lock of a transaction to decide its status . // The return values are ( ttl commitTS err ) : // If the transaction is active this function returns the ttl of the lock ; // If the transaction is committed this function returns the commitTS ; // If the transaction is rollbacked this function returns ( 0 0 nil ) // Note that CheckTxnStatus may also push forward the ` minCommitTS ` of the // transaction so it ' s not simply a read - only operation . // // primaryKey + lockTS together could locate the primary lock . // callerStartTS is the start ts of reader transaction . // currentTS is the current ts but it may be inaccurate . Just use it to check TTL .
func (mvcc *MVCCRocksDB) CheckTxnStatus(primaryKey []byte, lockTS uint64, startTS, currentTS uint64, rollbackIfNotFound bool) (ttl uint64, commitTS uint64, action kvrpcpb.Action, err error) {
	mvcc.mu.Lock()
	defer mvcc.mu.Unlock()
	action = kvrpcpb.Action_NoAction
	startKey := mvccEncode(primaryKey, lockVer)
	iter := newIterator(mvcc.db, &gorocksdb.Range{
		Start: startKey,
	})
	defer iter.Close()
	if iter.Valid() {
		dec := lockDecoder{
			expectKey: primaryKey,
		}
		var ok bool
		ok, err = dec.Decode(iter)
		if err != nil {
			err = errors.Trace(err)
			return
		}
		if ok && dec.lock.startTS == lockTS {
			lock := dec.lock
			batch := gorocksdb.NewWriteBatch()
			if uint64(oracle.ExtractPhysical(lock.startTS))+lock.ttl < uint64(oracle.ExtractPhysical(currentTS)) {
				if err = rollbackLock(batch, primaryKey, lockTS); err != nil {
					err = errors.Trace(err)
					return
				}
				if err = mvcc.db.Write(gorocksdb.NewDefaultWriteOptions(), batch); err != nil {
					err = errors.Trace(err)
					return
				}
				return 0, 0, kvrpcpb.Action_TTLExpireRollback, nil
			}
			if startTS == math.MaxUint64 {
				action = kvrpcpb.Action_MinCommitTSPushed
			} else if lock.minCommitTS > 0 {
				action = kvrpcpb.Action_MinCommitTSPushed
				if lock.minCommitTS < startTS+1 {
					lock.minCommitTS = startTS + 1
					if lock.minCommitTS < currentTS {
						lock.minCommitTS = currentTS
					}
					writeKey := mvccEncode(primaryKey, lockVer)
					writeValue, err1 := lock.MarshalBinary()
					if err1 != nil {
						err = errors.Trace(err1)
						return
					}
					batch.Put(writeKey, writeValue)
					if err1 = mvcc.db.Write(gorocksdb.NewDefaultWriteOptions(), batch); err1 != nil {
						err = errors.Trace(err1)
						return
					}
				}
			}
			return lock.ttl, 0, action, nil
		}
		c, ok, err1 := getTxnCommitInfo(iter, primaryKey, lockTS)
		if err1 != nil {
			err = errors.Trace(err1)
			return
		}
		if ok {
			if c.valueType != typeRollback {
				return 0, c.commitTS, action, nil
			}
			return 0, 0, kvrpcpb.Action_NoAction, nil
		}
	}
	if rollbackIfNotFound {
		batch := gorocksdb.NewWriteBatch()
		if err1 := writeRollback(batch, primaryKey, lockTS); err1 != nil {
			err = errors.Trace(err1)
			return
		}
		if err1 := mvcc.db.Write(gorocksdb.NewDefaultWriteOptions(), batch); err1 != nil {
			err = errors.Trace(err1)
			return
		}
		return 0, 0, kvrpcpb.Action_LockNotExistRollback, nil
	}
	return 0, 0, action, &ErrTxnNotFound{kvrpcpb.TxnNotFound{
		StartTs:    lockTS,
		PrimaryKey: primaryKey,
	}}
}
func ( mvcc * MVCCRocksDB ) CheckTxnStatus ( primaryKey [ ] byte , lockTS uint64 , startTS , currentTS uint64 , rollbackIfNotFound bool ) ( ttl uint64 , commitTS uint64 , action kvrpcpb . Action , err error ) { mvcc . mu . Lock ( ) defer mvcc . mu . Unlock ( ) action = kvrpcpb . Action_NoAction startKey := mvccEncode ( primaryKey , lockVer ) iter := newIterator ( mvcc . db , & gorocksdb . Range { Start : startKey , } ) defer iter . Close ( ) if iter . Valid ( ) { dec := lockDecoder { expectKey : primaryKey , } var ok bool ok , err = dec . Decode ( iter ) if err != nil { err = errors . Trace ( err ) return } if ok && dec . lock . startTS == lockTS { lock := dec . lock batch := gorocksdb . NewWriteBatch ( ) if uint64 ( oracle . ExtractPhysical ( lock . startTS ) ) + lock . ttl < uint64 ( oracle . ExtractPhysical ( currentTS ) ) { if err = rollbackLock ( batch , primaryKey , lockTS ) ; err != nil { err = errors . Trace ( err ) return } if err = mvcc . db . Write ( gorocksdb . NewDefaultWriteOptions ( ) , batch ) ; err != nil { err = errors . Trace ( err ) return } return 0 , 0 , kvrpcpb . Action_TTLExpireRollback , nil } if startTS == math . MaxUint64 { action = kvrpcpb . Action_MinCommitTSPushed } else if lock . minCommitTS > 0 { action = kvrpcpb . Action_MinCommitTSPushed if lock . minCommitTS < startTS + 1 { lock . minCommitTS = startTS + 1 if lock . minCommitTS < currentTS { lock . minCommitTS = currentTS } writeKey := mvccEncode ( primaryKey , lockVer ) writeValue , err1 := lock . MarshalBinary ( ) if err1 != nil { err = errors . Trace ( err1 ) return } batch . Put ( writeKey , writeValue ) if err1 = mvcc . db . Write ( gorocksdb . NewDefaultWriteOptions ( ) , batch ) ; err1 != nil { err = errors . Trace ( err1 ) return } } } return lock . ttl , 0 , action , nil } c , ok , err1 := getTxnCommitInfo ( iter , primaryKey , lockTS ) if err1 != nil { err = errors . Trace ( err1 ) return } if ok { if c . valueType != typeRollback { return 0 , c . commitTS , action , nil } return 0 , 0 , kvrpcpb . Action_NoAction , nil } } if rollbackIfNotFound { batch := gorocksdb . NewWriteBatch ( ) if err1 := writeRollback ( batch , primaryKey , lockTS ) ; err1 != nil { err = errors . Trace ( err1 ) return } if err1 := mvcc . db . Write ( gorocksdb . NewDefaultWriteOptions ( ) , batch ) ; err1 != nil { err = errors . Trace ( err1 ) return } return 0 , 0 , kvrpcpb . Action_LockNotExistRollback , nil } return 0 , 0 , action , & ErrTxnNotFound { kvrpcpb . TxnNotFound { StartTs : lockTS , PrimaryKey : primaryKey , } } }
CheckTxnStatus checks the primary lock of a transaction to decide its status.
CheckTxnStatus checks the primary lock of a transaction to decide its status .
// If current transaction's lock exists. // If the lock has already outdated, clean up it. // If the caller_start_ts is MaxUint64, it's a point get in the autocommit transaction. // Even though the MinCommitTs is not pushed, the point get can ingore the lock // next time because it's not committed. So we pretend it has been pushed. // If this is a large transaction and the lock is active, push forward the minCommitTS. // lock.minCommitTS == 0 may be a secondary lock, or not a large transaction (old version TiDB). // We *must* guarantee the invariance lock.minCommitTS >= callerStartTS + 1 // Remove this condition should not affect correctness. // We do it because pushing forward minCommitTS as far as possible could avoid // the lock been pushed again several times, and thus reduce write operations. // If current transaction's lock does not exist. // If the commit info of the current transaction exists. // If current transaction is already committed. // If current transaction is already rollback. // If current transaction is not prewritted before, it may be pessimistic lock. // When pessimistic txn rollback statement, it may not leave a 'rollbacked' tombstone. // Or maybe caused by concurrent prewrite operation. // Especially in the non-block reading case, the secondary lock is likely to be // written before the primary lock. // Write rollback record, but not delete the lock on the primary key. There may exist lock which has // different lock.startTS with input lockTS, for example the primary key could be already // locked by the caller transaction, deleting this key will mistakenly delete the lock on // primary key, see case TestSingleStatementRollback in session_test suite for example