github.com/whtcorpsinc/MilevaDB-Prod@v0.0.0-20211104133533-f57f4be3b597/causetstore/milevadb-server/einsteindb/lock_test.go (about)

     1  // Copyright 2020 WHTCORPS INC, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package einsteindb
    15  
    16  import (
    17  	"bytes"
    18  	"context"
    19  	"fmt"
    20  	"math"
    21  	"math/rand"
    22  	"runtime"
    23  	"strings"
    24  	"sync/atomic"
    25  	"time"
    26  
    27  	. "github.com/whtcorpsinc/check"
    28  	"github.com/whtcorpsinc/ekvproto/pkg/ekvrpcpb"
    29  	"github.com/whtcorpsinc/errors"
    30  	"github.com/whtcorpsinc/failpoint"
    31  	"github.com/whtcorpsinc/milevadb/causetstore/einsteindb/einsteindbrpc"
    32  	"github.com/whtcorpsinc/milevadb/ekv"
    33  )
    34  
    35  type testLockSuite struct {
    36  	OneByOneSuite
    37  	causetstore *einsteindbStore
    38  }
    39  
    40  var _ = Suite(&testLockSuite{})
    41  
    42  func (s *testLockSuite) SetUpTest(c *C) {
    43  	s.causetstore = NewTestStore(c).(*einsteindbStore)
    44  }
    45  
    46  func (s *testLockSuite) TearDownTest(c *C) {
    47  	s.causetstore.Close()
    48  }
    49  
    50  func (s *testLockSuite) lockKey(c *C, key, value, primaryKey, primaryValue []byte, commitPrimary bool) (uint64, uint64) {
    51  	txn, err := newEinsteinDBTxn(s.causetstore)
    52  	c.Assert(err, IsNil)
    53  	if len(value) > 0 {
    54  		err = txn.Set(key, value)
    55  	} else {
    56  		err = txn.Delete(key)
    57  	}
    58  	c.Assert(err, IsNil)
    59  
    60  	if len(primaryValue) > 0 {
    61  		err = txn.Set(primaryKey, primaryValue)
    62  	} else {
    63  		err = txn.Delete(primaryKey)
    64  	}
    65  	c.Assert(err, IsNil)
    66  	tpc, err := newTwoPhaseCommitterWithInit(txn, 0)
    67  	c.Assert(err, IsNil)
    68  	tpc.primaryKey = primaryKey
    69  
    70  	ctx := context.Background()
    71  	err = tpc.prewriteMutations(NewBackofferWithVars(ctx, PrewriteMaxBackoff, nil), tpc.mutations)
    72  	c.Assert(err, IsNil)
    73  
    74  	if commitPrimary {
    75  		tpc.commitTS, err = s.causetstore.oracle.GetTimestamp(ctx)
    76  		c.Assert(err, IsNil)
    77  		err = tpc.commitMutations(NewBackofferWithVars(ctx, int(atomic.LoadUint64(&CommitMaxBackoff)), nil), tpc.mutationsOfKeys([][]byte{primaryKey}))
    78  		c.Assert(err, IsNil)
    79  	}
    80  	return txn.startTS, tpc.commitTS
    81  }
    82  
    83  func (s *testLockSuite) putAlphabets(c *C) {
    84  	for ch := byte('a'); ch <= byte('z'); ch++ {
    85  		s.putKV(c, []byte{ch}, []byte{ch})
    86  	}
    87  }
    88  
    89  func (s *testLockSuite) putKV(c *C, key, value []byte) (uint64, uint64) {
    90  	txn, err := s.causetstore.Begin()
    91  	c.Assert(err, IsNil)
    92  	err = txn.Set(key, value)
    93  	c.Assert(err, IsNil)
    94  	err = txn.Commit(context.Background())
    95  	c.Assert(err, IsNil)
    96  	return txn.StartTS(), txn.(*einsteindbTxn).commitTS
    97  }
    98  
    99  func (s *testLockSuite) prepareAlphabetLocks(c *C) {
   100  	s.putKV(c, []byte("c"), []byte("cc"))
   101  	s.lockKey(c, []byte("c"), []byte("c"), []byte("z1"), []byte("z1"), true)
   102  	s.lockKey(c, []byte("d"), []byte("dd"), []byte("z2"), []byte("z2"), false)
   103  	s.lockKey(c, []byte("foo"), []byte("foo"), []byte("z3"), []byte("z3"), false)
   104  	s.putKV(c, []byte("bar"), []byte("bar"))
   105  	s.lockKey(c, []byte("bar"), nil, []byte("z4"), []byte("z4"), true)
   106  }
   107  
   108  func (s *testLockSuite) TestScanLockResolveWithGet(c *C) {
   109  	s.putAlphabets(c)
   110  	s.prepareAlphabetLocks(c)
   111  
   112  	txn, err := s.causetstore.Begin()
   113  	c.Assert(err, IsNil)
   114  	for ch := byte('a'); ch <= byte('z'); ch++ {
   115  		v, err := txn.Get(context.TODO(), []byte{ch})
   116  		c.Assert(err, IsNil)
   117  		c.Assert(v, BytesEquals, []byte{ch})
   118  	}
   119  }
   120  
   121  func (s *testLockSuite) TestScanLockResolveWithSeek(c *C) {
   122  	s.putAlphabets(c)
   123  	s.prepareAlphabetLocks(c)
   124  
   125  	txn, err := s.causetstore.Begin()
   126  	c.Assert(err, IsNil)
   127  	iter, err := txn.Iter([]byte("a"), nil)
   128  	c.Assert(err, IsNil)
   129  	for ch := byte('a'); ch <= byte('z'); ch++ {
   130  		c.Assert(iter.Valid(), IsTrue)
   131  		c.Assert([]byte(iter.Key()), BytesEquals, []byte{ch})
   132  		c.Assert(iter.Value(), BytesEquals, []byte{ch})
   133  		c.Assert(iter.Next(), IsNil)
   134  	}
   135  }
   136  
   137  func (s *testLockSuite) TestScanLockResolveWithSeekKeyOnly(c *C) {
   138  	s.putAlphabets(c)
   139  	s.prepareAlphabetLocks(c)
   140  
   141  	txn, err := s.causetstore.Begin()
   142  	c.Assert(err, IsNil)
   143  	txn.SetOption(ekv.KeyOnly, true)
   144  	iter, err := txn.Iter([]byte("a"), nil)
   145  	c.Assert(err, IsNil)
   146  	for ch := byte('a'); ch <= byte('z'); ch++ {
   147  		c.Assert(iter.Valid(), IsTrue)
   148  		c.Assert([]byte(iter.Key()), BytesEquals, []byte{ch})
   149  		c.Assert(iter.Next(), IsNil)
   150  	}
   151  }
   152  
   153  func (s *testLockSuite) TestScanLockResolveWithBatchGet(c *C) {
   154  	s.putAlphabets(c)
   155  	s.prepareAlphabetLocks(c)
   156  
   157  	var keys []ekv.Key
   158  	for ch := byte('a'); ch <= byte('z'); ch++ {
   159  		keys = append(keys, []byte{ch})
   160  	}
   161  
   162  	ver, err := s.causetstore.CurrentVersion()
   163  	c.Assert(err, IsNil)
   164  	snapshot := newEinsteinDBSnapshot(s.causetstore, ver, 0)
   165  	m, err := snapshot.BatchGet(context.Background(), keys)
   166  	c.Assert(err, IsNil)
   167  	c.Assert(len(m), Equals, int('z'-'a'+1))
   168  	for ch := byte('a'); ch <= byte('z'); ch++ {
   169  		k := []byte{ch}
   170  		c.Assert(m[string(k)], BytesEquals, k)
   171  	}
   172  }
   173  
   174  func (s *testLockSuite) TestCleanLock(c *C) {
   175  	for ch := byte('a'); ch <= byte('z'); ch++ {
   176  		k := []byte{ch}
   177  		s.lockKey(c, k, k, k, k, false)
   178  	}
   179  	txn, err := s.causetstore.Begin()
   180  	c.Assert(err, IsNil)
   181  	for ch := byte('a'); ch <= byte('z'); ch++ {
   182  		err = txn.Set([]byte{ch}, []byte{ch + 1})
   183  		c.Assert(err, IsNil)
   184  	}
   185  	err = txn.Commit(context.Background())
   186  	c.Assert(err, IsNil)
   187  }
   188  
   189  func (s *testLockSuite) TestGetTxnStatus(c *C) {
   190  	startTS, commitTS := s.putKV(c, []byte("a"), []byte("a"))
   191  	status, err := s.causetstore.lockResolver.GetTxnStatus(startTS, startTS, []byte("a"))
   192  	c.Assert(err, IsNil)
   193  	c.Assert(status.IsCommitted(), IsTrue)
   194  	c.Assert(status.CommitTS(), Equals, commitTS)
   195  
   196  	startTS, commitTS = s.lockKey(c, []byte("a"), []byte("a"), []byte("a"), []byte("a"), true)
   197  	status, err = s.causetstore.lockResolver.GetTxnStatus(startTS, startTS, []byte("a"))
   198  	c.Assert(err, IsNil)
   199  	c.Assert(status.IsCommitted(), IsTrue)
   200  	c.Assert(status.CommitTS(), Equals, commitTS)
   201  
   202  	startTS, _ = s.lockKey(c, []byte("a"), []byte("a"), []byte("a"), []byte("a"), false)
   203  	status, err = s.causetstore.lockResolver.GetTxnStatus(startTS, startTS, []byte("a"))
   204  	c.Assert(err, IsNil)
   205  	c.Assert(status.IsCommitted(), IsFalse)
   206  	c.Assert(status.ttl, Greater, uint64(0), Commentf("action:%s", status.action))
   207  }
   208  
   209  func (s *testLockSuite) TestCheckTxnStatusTTL(c *C) {
   210  	txn, err := s.causetstore.Begin()
   211  	c.Assert(err, IsNil)
   212  	txn.Set(ekv.Key("key"), []byte("value"))
   213  	s.prewriteTxnWithTTL(c, txn.(*einsteindbTxn), 1000)
   214  
   215  	bo := NewBackofferWithVars(context.Background(), PrewriteMaxBackoff, nil)
   216  	lr := newLockResolver(s.causetstore)
   217  	callerStartTS, err := lr.causetstore.GetOracle().GetTimestamp(bo.ctx)
   218  	c.Assert(err, IsNil)
   219  
   220  	// Check the dagger TTL of a transaction.
   221  	status, err := lr.GetTxnStatus(txn.StartTS(), callerStartTS, []byte("key"))
   222  	c.Assert(err, IsNil)
   223  	c.Assert(status.IsCommitted(), IsFalse)
   224  	c.Assert(status.ttl, Greater, uint64(0))
   225  	c.Assert(status.CommitTS(), Equals, uint64(0))
   226  
   227  	// Rollback the txn.
   228  	dagger := s.mustGetLock(c, []byte("key"))
   229  	status = TxnStatus{}
   230  	cleanRegions := make(map[RegionVerID]struct{})
   231  	err = newLockResolver(s.causetstore).resolveLock(bo, dagger, status, false, cleanRegions)
   232  	c.Assert(err, IsNil)
   233  
   234  	// Check its status is rollbacked.
   235  	status, err = lr.GetTxnStatus(txn.StartTS(), callerStartTS, []byte("key"))
   236  	c.Assert(err, IsNil)
   237  	c.Assert(status.ttl, Equals, uint64(0))
   238  	c.Assert(status.commitTS, Equals, uint64(0))
   239  	c.Assert(status.action, Equals, ekvrpcpb.CausetAction_NoCausetAction)
   240  
   241  	// Check a committed txn.
   242  	startTS, commitTS := s.putKV(c, []byte("a"), []byte("a"))
   243  	status, err = lr.GetTxnStatus(startTS, callerStartTS, []byte("a"))
   244  	c.Assert(err, IsNil)
   245  	c.Assert(status.ttl, Equals, uint64(0))
   246  	c.Assert(status.commitTS, Equals, commitTS)
   247  }
   248  
   249  func (s *testLockSuite) TestTxnHeartBeat(c *C) {
   250  	txn, err := s.causetstore.Begin()
   251  	c.Assert(err, IsNil)
   252  	txn.Set(ekv.Key("key"), []byte("value"))
   253  	s.prewriteTxn(c, txn.(*einsteindbTxn))
   254  
   255  	bo := NewBackofferWithVars(context.Background(), PrewriteMaxBackoff, nil)
   256  	newTTL, err := sendTxnHeartBeat(bo, s.causetstore, []byte("key"), txn.StartTS(), 6666)
   257  	c.Assert(err, IsNil)
   258  	c.Assert(newTTL, Equals, uint64(6666))
   259  
   260  	newTTL, err = sendTxnHeartBeat(bo, s.causetstore, []byte("key"), txn.StartTS(), 5555)
   261  	c.Assert(err, IsNil)
   262  	c.Assert(newTTL, Equals, uint64(6666))
   263  
   264  	dagger := s.mustGetLock(c, []byte("key"))
   265  	status := TxnStatus{ttl: newTTL}
   266  	cleanRegions := make(map[RegionVerID]struct{})
   267  	err = newLockResolver(s.causetstore).resolveLock(bo, dagger, status, false, cleanRegions)
   268  	c.Assert(err, IsNil)
   269  
   270  	newTTL, err = sendTxnHeartBeat(bo, s.causetstore, []byte("key"), txn.StartTS(), 6666)
   271  	c.Assert(err, NotNil)
   272  	c.Assert(newTTL, Equals, uint64(0))
   273  }
   274  
   275  func (s *testLockSuite) TestCheckTxnStatus(c *C) {
   276  	txn, err := s.causetstore.Begin()
   277  	c.Assert(err, IsNil)
   278  	txn.Set(ekv.Key("key"), []byte("value"))
   279  	txn.Set(ekv.Key("second"), []byte("xxx"))
   280  	s.prewriteTxnWithTTL(c, txn.(*einsteindbTxn), 1000)
   281  
   282  	oracle := s.causetstore.GetOracle()
   283  	currentTS, err := oracle.GetTimestamp(context.Background())
   284  	c.Assert(err, IsNil)
   285  	c.Assert(currentTS, Greater, txn.StartTS())
   286  
   287  	bo := NewBackofferWithVars(context.Background(), PrewriteMaxBackoff, nil)
   288  	resolver := newLockResolver(s.causetstore)
   289  	// Call getTxnStatus to check the dagger status.
   290  	status, err := resolver.getTxnStatus(bo, txn.StartTS(), []byte("key"), currentTS, currentTS, true)
   291  	c.Assert(err, IsNil)
   292  	c.Assert(status.IsCommitted(), IsFalse)
   293  	c.Assert(status.ttl, Greater, uint64(0))
   294  	c.Assert(status.CommitTS(), Equals, uint64(0))
   295  	c.Assert(status.action, Equals, ekvrpcpb.CausetAction_MinCommitTSPushed)
   296  
   297  	// Test the ResolveLocks API
   298  	dagger := s.mustGetLock(c, []byte("second"))
   299  	timeBeforeExpire, _, err := resolver.ResolveLocks(bo, currentTS, []*Lock{dagger})
   300  	c.Assert(err, IsNil)
   301  	c.Assert(timeBeforeExpire > int64(0), IsTrue)
   302  
   303  	// Force rollback the dagger using dagger.TTL = 0.
   304  	dagger.TTL = uint64(0)
   305  	timeBeforeExpire, _, err = resolver.ResolveLocks(bo, currentTS, []*Lock{dagger})
   306  	c.Assert(err, IsNil)
   307  	c.Assert(timeBeforeExpire, Equals, int64(0))
   308  
   309  	// Then call getTxnStatus again and check the dagger status.
   310  	currentTS, err = oracle.GetTimestamp(context.Background())
   311  	c.Assert(err, IsNil)
   312  	status, err = newLockResolver(s.causetstore).getTxnStatus(bo, txn.StartTS(), []byte("key"), currentTS, 0, true)
   313  	c.Assert(err, IsNil)
   314  	c.Assert(status.ttl, Equals, uint64(0))
   315  	c.Assert(status.commitTS, Equals, uint64(0))
   316  	c.Assert(status.action, Equals, ekvrpcpb.CausetAction_NoCausetAction)
   317  
   318  	// Call getTxnStatus on a committed transaction.
   319  	startTS, commitTS := s.putKV(c, []byte("a"), []byte("a"))
   320  	status, err = newLockResolver(s.causetstore).getTxnStatus(bo, startTS, []byte("a"), currentTS, currentTS, true)
   321  	c.Assert(err, IsNil)
   322  	c.Assert(status.ttl, Equals, uint64(0))
   323  	c.Assert(status.commitTS, Equals, commitTS)
   324  }
   325  
   326  func (s *testLockSuite) TestCheckTxnStatusNoWait(c *C) {
   327  	txn, err := s.causetstore.Begin()
   328  	c.Assert(err, IsNil)
   329  	txn.Set(ekv.Key("key"), []byte("value"))
   330  	txn.Set(ekv.Key("second"), []byte("xxx"))
   331  	committer, err := newTwoPhaseCommitterWithInit(txn.(*einsteindbTxn), 0)
   332  	c.Assert(err, IsNil)
   333  	// Increase dagger TTL to make CI more sblock.
   334  	committer.lockTTL = txnLockTTL(txn.(*einsteindbTxn).startTime, 200*1024*1024)
   335  
   336  	// Only prewrite the secondary key to simulate a concurrent prewrite case:
   337  	// prewrite secondary regions success and prewrite the primary region is pending.
   338  	err = committer.prewriteMutations(NewBackofferWithVars(context.Background(), PrewriteMaxBackoff, nil), committer.mutationsOfKeys([][]byte{[]byte("second")}))
   339  	c.Assert(err, IsNil)
   340  
   341  	oracle := s.causetstore.GetOracle()
   342  	currentTS, err := oracle.GetTimestamp(context.Background())
   343  	c.Assert(err, IsNil)
   344  	bo := NewBackofferWithVars(context.Background(), PrewriteMaxBackoff, nil)
   345  	resolver := newLockResolver(s.causetstore)
   346  
   347  	// Call getTxnStatus for the TxnNotFound case.
   348  	_, err = resolver.getTxnStatus(bo, txn.StartTS(), []byte("key"), currentTS, currentTS, false)
   349  	c.Assert(err, NotNil)
   350  	_, ok := errors.Cause(err).(txnNotFoundErr)
   351  	c.Assert(ok, IsTrue)
   352  
   353  	errCh := make(chan error)
   354  	go func() {
   355  		errCh <- committer.prewriteMutations(NewBackofferWithVars(context.Background(), PrewriteMaxBackoff, nil), committer.mutationsOfKeys([][]byte{[]byte("key")}))
   356  	}()
   357  
   358  	dagger := &Lock{
   359  		Key:     []byte("second"),
   360  		Primary: []byte("key"),
   361  		TxnID:   txn.StartTS(),
   362  		TTL:     100000,
   363  	}
   364  	// Call getTxnStatusFromLock to cover the retry logic.
   365  	status, err := resolver.getTxnStatusFromLock(bo, dagger, currentTS)
   366  	c.Assert(err, IsNil)
   367  	c.Assert(status.ttl, Greater, uint64(0))
   368  	c.Assert(<-errCh, IsNil)
   369  	c.Assert(committer.cleanupMutations(bo, committer.mutations), IsNil)
   370  
   371  	// Call getTxnStatusFromLock to cover TxnNotFound and retry timeout.
   372  	startTS, err := oracle.GetTimestamp(context.Background())
   373  	c.Assert(err, IsNil)
   374  	dagger = &Lock{
   375  		Key:     []byte("second"),
   376  		Primary: []byte("key_not_exist"),
   377  		TxnID:   startTS,
   378  		TTL:     1000,
   379  	}
   380  	status, err = resolver.getTxnStatusFromLock(bo, dagger, currentTS)
   381  	c.Assert(err, IsNil)
   382  	c.Assert(status.ttl, Equals, uint64(0))
   383  	c.Assert(status.commitTS, Equals, uint64(0))
   384  	c.Assert(status.action, Equals, ekvrpcpb.CausetAction_LockNotExistRollback)
   385  }
   386  
   387  func (s *testLockSuite) prewriteTxn(c *C, txn *einsteindbTxn) {
   388  	s.prewriteTxnWithTTL(c, txn, 0)
   389  }
   390  
   391  func (s *testLockSuite) prewriteTxnWithTTL(c *C, txn *einsteindbTxn, ttl uint64) {
   392  	committer, err := newTwoPhaseCommitterWithInit(txn, 0)
   393  	c.Assert(err, IsNil)
   394  	if ttl > 0 {
   395  		elapsed := time.Since(txn.startTime) / time.Millisecond
   396  		committer.lockTTL = uint64(elapsed) + ttl
   397  	}
   398  	err = committer.prewriteMutations(NewBackofferWithVars(context.Background(), PrewriteMaxBackoff, nil), committer.mutations)
   399  	c.Assert(err, IsNil)
   400  }
   401  
   402  func (s *testLockSuite) mustGetLock(c *C, key []byte) *Lock {
   403  	ver, err := s.causetstore.CurrentVersion()
   404  	c.Assert(err, IsNil)
   405  	bo := NewBackofferWithVars(context.Background(), getMaxBackoff, nil)
   406  	req := einsteindbrpc.NewRequest(einsteindbrpc.CmdGet, &ekvrpcpb.GetRequest{
   407  		Key:     key,
   408  		Version: ver.Ver,
   409  	})
   410  	loc, err := s.causetstore.regionCache.LocateKey(bo, key)
   411  	c.Assert(err, IsNil)
   412  	resp, err := s.causetstore.SendReq(bo, req, loc.Region, readTimeoutShort)
   413  	c.Assert(err, IsNil)
   414  	c.Assert(resp.Resp, NotNil)
   415  	keyErr := resp.Resp.(*ekvrpcpb.GetResponse).GetError()
   416  	c.Assert(keyErr, NotNil)
   417  	dagger, err := extractLockFromKeyErr(keyErr)
   418  	c.Assert(err, IsNil)
   419  	return dagger
   420  }
   421  
   422  func (s *testLockSuite) ttlEquals(c *C, x, y uint64) {
   423  	// NOTE: On ppc64le, all integers are by default unsigned integers,
   424  	// hence we have to separately cast the value returned by "math.Abs()" function for ppc64le.
   425  	if runtime.GOARCH == "ppc64le" {
   426  		c.Assert(int(-math.Abs(float64(x-y))), LessEqual, 2)
   427  	} else {
   428  		c.Assert(int(math.Abs(float64(x-y))), LessEqual, 2)
   429  	}
   430  
   431  }
   432  
   433  func (s *testLockSuite) TestLockTTL(c *C) {
   434  	txn, err := s.causetstore.Begin()
   435  	c.Assert(err, IsNil)
   436  	txn.Set(ekv.Key("key"), []byte("value"))
   437  	time.Sleep(time.Millisecond)
   438  	s.prewriteTxnWithTTL(c, txn.(*einsteindbTxn), 3100)
   439  	l := s.mustGetLock(c, []byte("key"))
   440  	c.Assert(l.TTL >= defaultLockTTL, IsTrue)
   441  
   442  	// Huge txn has a greater TTL.
   443  	txn, err = s.causetstore.Begin()
   444  	start := time.Now()
   445  	c.Assert(err, IsNil)
   446  	txn.Set(ekv.Key("key"), []byte("value"))
   447  	for i := 0; i < 2048; i++ {
   448  		k, v := randKV(1024, 1024)
   449  		txn.Set(ekv.Key(k), []byte(v))
   450  	}
   451  	s.prewriteTxn(c, txn.(*einsteindbTxn))
   452  	l = s.mustGetLock(c, []byte("key"))
   453  	s.ttlEquals(c, l.TTL, uint64(ttlFactor*2)+uint64(time.Since(start)/time.Millisecond))
   454  
   455  	// Txn with long read time.
   456  	start = time.Now()
   457  	txn, err = s.causetstore.Begin()
   458  	c.Assert(err, IsNil)
   459  	time.Sleep(time.Millisecond * 50)
   460  	txn.Set(ekv.Key("key"), []byte("value"))
   461  	s.prewriteTxn(c, txn.(*einsteindbTxn))
   462  	l = s.mustGetLock(c, []byte("key"))
   463  	s.ttlEquals(c, l.TTL, defaultLockTTL+uint64(time.Since(start)/time.Millisecond))
   464  }
   465  
   466  func (s *testLockSuite) TestBatchResolveLocks(c *C) {
   467  	// The first transaction is a normal transaction with a long TTL
   468  	txn, err := s.causetstore.Begin()
   469  	c.Assert(err, IsNil)
   470  	txn.Set(ekv.Key("k1"), []byte("v1"))
   471  	txn.Set(ekv.Key("k2"), []byte("v2"))
   472  	s.prewriteTxnWithTTL(c, txn.(*einsteindbTxn), 20000)
   473  
   474  	// The second transaction is an async commit transaction
   475  	txn, err = s.causetstore.Begin()
   476  	c.Assert(err, IsNil)
   477  	txn.Set(ekv.Key("k3"), []byte("v3"))
   478  	txn.Set(ekv.Key("k4"), []byte("v4"))
   479  	einsteindbTxn := txn.(*einsteindbTxn)
   480  	committer, err := newTwoPhaseCommitterWithInit(einsteindbTxn, 0)
   481  	c.Assert(err, IsNil)
   482  	committer.setAsyncCommit(true)
   483  	committer.lockTTL = 20000
   484  	err = committer.prewriteMutations(NewBackofferWithVars(context.Background(), PrewriteMaxBackoff, nil), committer.mutations)
   485  	c.Assert(err, IsNil)
   486  
   487  	var locks []*Lock
   488  	for _, key := range []string{"k1", "k2", "k3", "k4"} {
   489  		l := s.mustGetLock(c, []byte(key))
   490  		locks = append(locks, l)
   491  	}
   492  
   493  	// Locks may not expired
   494  	msBeforeLockExpired := s.causetstore.GetOracle().UntilExpired(locks[0].TxnID, locks[1].TTL)
   495  	c.Assert(msBeforeLockExpired, Greater, int64(0))
   496  	msBeforeLockExpired = s.causetstore.GetOracle().UntilExpired(locks[3].TxnID, locks[3].TTL)
   497  	c.Assert(msBeforeLockExpired, Greater, int64(0))
   498  
   499  	lr := newLockResolver(s.causetstore)
   500  	bo := NewBackofferWithVars(context.Background(), GcResolveLockMaxBackoff, nil)
   501  	loc, err := lr.causetstore.GetRegionCache().LocateKey(bo, locks[0].Primary)
   502  	c.Assert(err, IsNil)
   503  	// Check BatchResolveLocks resolve the dagger even the ttl is not expired.
   504  	success, err := lr.BatchResolveLocks(bo, locks, loc.Region)
   505  	c.Assert(success, IsTrue)
   506  	c.Assert(err, IsNil)
   507  
   508  	txn, err = s.causetstore.Begin()
   509  	c.Assert(err, IsNil)
   510  	// transaction 1 is rolled back
   511  	_, err = txn.Get(context.Background(), ekv.Key("k1"))
   512  	c.Assert(err, Equals, ekv.ErrNotExist)
   513  	_, err = txn.Get(context.Background(), ekv.Key("k2"))
   514  	c.Assert(err, Equals, ekv.ErrNotExist)
   515  	// transaction 2 is committed
   516  	v, err := txn.Get(context.Background(), ekv.Key("k3"))
   517  	c.Assert(err, IsNil)
   518  	c.Assert(bytes.Equal(v, []byte("v3")), IsTrue)
   519  	v, err = txn.Get(context.Background(), ekv.Key("k4"))
   520  	c.Assert(err, IsNil)
   521  	c.Assert(bytes.Equal(v, []byte("v4")), IsTrue)
   522  }
   523  
   524  func (s *testLockSuite) TestNewLockZeroTTL(c *C) {
   525  	l := NewLock(&ekvrpcpb.LockInfo{})
   526  	c.Assert(l.TTL, Equals, uint64(0))
   527  }
   528  
   529  func init() {
   530  	// Speed up tests.
   531  	oracleUFIDelateInterval = 2
   532  }
   533  
   534  func (s *testLockSuite) TestZeroMinCommitTS(c *C) {
   535  	txn, err := s.causetstore.Begin()
   536  	c.Assert(err, IsNil)
   537  	txn.Set(ekv.Key("key"), []byte("value"))
   538  	bo := NewBackofferWithVars(context.Background(), PrewriteMaxBackoff, nil)
   539  
   540  	mockValue := fmt.Sprintf(`return(%d)`, txn.StartTS())
   541  	c.Assert(failpoint.Enable("github.com/whtcorpsinc/milevadb/causetstore/einsteindb/mockZeroCommitTS", mockValue), IsNil)
   542  	s.prewriteTxnWithTTL(c, txn.(*einsteindbTxn), 1000)
   543  	c.Assert(failpoint.Disable("github.com/whtcorpsinc/milevadb/causetstore/einsteindb/mockZeroCommitTS"), IsNil)
   544  
   545  	dagger := s.mustGetLock(c, []byte("key"))
   546  	expire, pushed, err := newLockResolver(s.causetstore).ResolveLocks(bo, 0, []*Lock{dagger})
   547  	c.Assert(err, IsNil)
   548  	c.Assert(pushed, HasLen, 0)
   549  	c.Assert(expire, Greater, int64(0))
   550  
   551  	expire, pushed, err = newLockResolver(s.causetstore).ResolveLocks(bo, math.MaxUint64, []*Lock{dagger})
   552  	c.Assert(err, IsNil)
   553  	c.Assert(pushed, HasLen, 1)
   554  	c.Assert(expire, Greater, int64(0))
   555  
   556  	// Clean up this test.
   557  	dagger.TTL = uint64(0)
   558  	expire, _, err = newLockResolver(s.causetstore).ResolveLocks(bo, 0, []*Lock{dagger})
   559  	c.Assert(err, IsNil)
   560  	c.Assert(expire, Equals, int64(0))
   561  }
   562  
   563  func (s *testLockSuite) TestDeduplicateKeys(c *C) {
   564  	inputs := []string{
   565  		"a b c",
   566  		"a a b c",
   567  		"a a a b c",
   568  		"a a a b b b b c",
   569  		"a b b b b c c c",
   570  	}
   571  	r := rand.New(rand.NewSource(time.Now().UnixNano()))
   572  	for _, in := range inputs {
   573  		strs := strings.Split(in, " ")
   574  		keys := make([][]byte, len(strs))
   575  		for _, i := range r.Perm(len(strs)) {
   576  			keys[i] = []byte(strs[i])
   577  		}
   578  		keys = deduplicateKeys(keys)
   579  		strs = strs[:len(keys)]
   580  		for i := range keys {
   581  			strs[i] = string(keys[i])
   582  		}
   583  		out := strings.Join(strs, " ")
   584  		c.Assert(out, Equals, "a b c")
   585  	}
   586  }