github.com/whtcorpsinc/MilevaDB-Prod@v0.0.0-20211104133533-f57f4be3b597/causetstore/milevadb-server/einsteindb/async_commit_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  	"sync/atomic"
    21  	"time"
    22  
    23  	. "github.com/whtcorpsinc/check"
    24  	"github.com/whtcorpsinc/ekvproto/pkg/ekvrpcpb"
    25  	"github.com/whtcorpsinc/errors"
    26  	"github.com/whtcorpsinc/milevadb/causetstore/einsteindb/einsteindbrpc"
    27  	"github.com/whtcorpsinc/milevadb/causetstore/mockstore/cluster"
    28  	"github.com/whtcorpsinc/milevadb/causetstore/mockstore/mockeinsteindb"
    29  	"github.com/whtcorpsinc/milevadb/config"
    30  )
    31  
    32  type testAsyncCommitSuite struct {
    33  	OneByOneSuite
    34  	cluster     cluster.Cluster
    35  	causetstore *einsteindbStore
    36  	bo          *Backoffer
    37  }
    38  
    39  var _ = Suite(&testAsyncCommitSuite{})
    40  
    41  func (s *testAsyncCommitSuite) SetUpTest(c *C) {
    42  	client, clstr, FIDelClient, err := mockeinsteindb.NewEinsteinDBAndFIDelClient("")
    43  	c.Assert(err, IsNil)
    44  	mockeinsteindb.BootstrapWithSingleStore(clstr)
    45  	s.cluster = clstr
    46  	causetstore, err := NewTestEinsteinDBStore(client, FIDelClient, nil, nil, 0)
    47  	c.Assert(err, IsNil)
    48  
    49  	s.causetstore = causetstore.(*einsteindbStore)
    50  	s.bo = NewBackofferWithVars(context.Background(), 5000, nil)
    51  }
    52  
    53  func (s *testAsyncCommitSuite) putAlphabets(c *C) {
    54  	for ch := byte('a'); ch <= byte('z'); ch++ {
    55  		s.putKV(c, []byte{ch}, []byte{ch})
    56  	}
    57  }
    58  
    59  func (s *testAsyncCommitSuite) putKV(c *C, key, value []byte) (uint64, uint64) {
    60  	txn, err := s.causetstore.Begin()
    61  	c.Assert(err, IsNil)
    62  	err = txn.Set(key, value)
    63  	c.Assert(err, IsNil)
    64  	err = txn.Commit(context.Background())
    65  	c.Assert(err, IsNil)
    66  	return txn.StartTS(), txn.(*einsteindbTxn).commitTS
    67  }
    68  
    69  func (s *testAsyncCommitSuite) lockKeys(c *C, keys, values [][]byte, primaryKey, primaryValue []byte, commitPrimary bool) (uint64, uint64) {
    70  	txn, err := newEinsteinDBTxn(s.causetstore)
    71  	c.Assert(err, IsNil)
    72  	for i, k := range keys {
    73  		if len(values[i]) > 0 {
    74  			err = txn.Set(k, values[i])
    75  		} else {
    76  			err = txn.Delete(k)
    77  		}
    78  		c.Assert(err, IsNil)
    79  	}
    80  	if len(primaryValue) > 0 {
    81  		err = txn.Set(primaryKey, primaryValue)
    82  	} else {
    83  		err = txn.Delete(primaryKey)
    84  	}
    85  	c.Assert(err, IsNil)
    86  	tpc, err := newTwoPhaseCommitterWithInit(txn, 0)
    87  	c.Assert(err, IsNil)
    88  	tpc.primaryKey = primaryKey
    89  
    90  	ctx := context.Background()
    91  	err = tpc.prewriteMutations(NewBackofferWithVars(ctx, PrewriteMaxBackoff, nil), tpc.mutations)
    92  	c.Assert(err, IsNil)
    93  
    94  	if commitPrimary {
    95  		tpc.commitTS, err = s.causetstore.oracle.GetTimestamp(ctx)
    96  		c.Assert(err, IsNil)
    97  		err = tpc.commitMutations(NewBackofferWithVars(ctx, int(atomic.LoadUint64(&CommitMaxBackoff)), nil), tpc.mutationsOfKeys([][]byte{primaryKey}))
    98  		c.Assert(err, IsNil)
    99  	}
   100  	return txn.startTS, tpc.commitTS
   101  }
   102  
   103  func (s *testAsyncCommitSuite) mustGetLock(c *C, key []byte) *Lock {
   104  	ver, err := s.causetstore.CurrentVersion()
   105  	c.Assert(err, IsNil)
   106  	req := einsteindbrpc.NewRequest(einsteindbrpc.CmdGet, &ekvrpcpb.GetRequest{
   107  		Key:     key,
   108  		Version: ver.Ver,
   109  	})
   110  	loc, err := s.causetstore.regionCache.LocateKey(s.bo, key)
   111  	c.Assert(err, IsNil)
   112  	resp, err := s.causetstore.SendReq(s.bo, req, loc.Region, readTimeoutShort)
   113  	c.Assert(err, IsNil)
   114  	c.Assert(resp.Resp, NotNil)
   115  	keyErr := resp.Resp.(*ekvrpcpb.GetResponse).GetError()
   116  	c.Assert(keyErr, NotNil)
   117  	dagger, err := extractLockFromKeyErr(keyErr)
   118  	c.Assert(err, IsNil)
   119  	return dagger
   120  }
   121  
   122  func (s *testAsyncCommitSuite) TestCheckSecondaries(c *C) {
   123  	config.UFIDelateGlobal(func(conf *config.Config) {
   124  		conf.EinsteinDBClient.EnableAsyncCommit = true
   125  	})
   126  	defer config.RestoreFunc()()
   127  
   128  	s.putAlphabets(c)
   129  
   130  	loc, err := s.causetstore.GetRegionCache().LocateKey(s.bo, []byte("a"))
   131  	c.Assert(err, IsNil)
   132  	newRegionID, peerID := s.cluster.AllocID(), s.cluster.AllocID()
   133  	s.cluster.Split(loc.Region.id, newRegionID, []byte("e"), []uint64{peerID}, peerID)
   134  	s.causetstore.GetRegionCache().InvalidateCachedRegion(loc.Region)
   135  
   136  	// No locks to check, only primary key is locked, should be successful.
   137  	s.lockKeys(c, [][]byte{}, [][]byte{}, []byte("z"), []byte("z"), false)
   138  	dagger := s.mustGetLock(c, []byte("z"))
   139  	dagger.UseAsyncCommit = true
   140  	ts, err := s.causetstore.oracle.GetTimestamp(context.Background())
   141  	c.Assert(err, IsNil)
   142  	status := TxnStatus{primaryLock: &ekvrpcpb.LockInfo{Secondaries: [][]byte{}, UseAsyncCommit: true, MinCommitTs: ts}}
   143  
   144  	err = s.causetstore.lockResolver.resolveLockAsync(s.bo, dagger, status)
   145  	c.Assert(err, IsNil)
   146  	currentTS, err := s.causetstore.oracle.GetTimestamp(context.Background())
   147  	c.Assert(err, IsNil)
   148  	status, err = s.causetstore.lockResolver.getTxnStatus(s.bo, dagger.TxnID, []byte("z"), currentTS, currentTS, true)
   149  	c.Assert(err, IsNil)
   150  	c.Assert(status.IsCommitted(), IsTrue)
   151  	c.Assert(status.CommitTS(), Equals, ts)
   152  
   153  	// One key is committed (i), one key is locked (a). Should get committed.
   154  	ts, err = s.causetstore.oracle.GetTimestamp(context.Background())
   155  	c.Assert(err, IsNil)
   156  	commitTs := ts + 10
   157  
   158  	gotCheckA := int64(0)
   159  	gotCheckB := int64(0)
   160  	gotResolve := int64(0)
   161  	gotOther := int64(0)
   162  	mock := mockResolveClient{
   163  		inner: s.causetstore.client,
   164  		onCheckSecondaries: func(req *ekvrpcpb.CheckSecondaryLocksRequest) (*einsteindbrpc.Response, error) {
   165  			if req.StartVersion != ts {
   166  				return nil, errors.Errorf("Bad start version: %d, expected: %d", req.StartVersion, ts)
   167  			}
   168  			var resp ekvrpcpb.CheckSecondaryLocksResponse
   169  			for _, k := range req.Keys {
   170  				if bytes.Equal(k, []byte("a")) {
   171  					atomic.StoreInt64(&gotCheckA, 1)
   172  
   173  					resp = ekvrpcpb.CheckSecondaryLocksResponse{
   174  						Locks:    []*ekvrpcpb.LockInfo{{Key: []byte("a"), PrimaryLock: []byte("z"), LockVersion: ts}},
   175  						CommitTs: commitTs,
   176  					}
   177  				} else if bytes.Equal(k, []byte("i")) {
   178  					atomic.StoreInt64(&gotCheckB, 1)
   179  
   180  					resp = ekvrpcpb.CheckSecondaryLocksResponse{
   181  						Locks:    []*ekvrpcpb.LockInfo{},
   182  						CommitTs: commitTs,
   183  					}
   184  				} else {
   185  					fmt.Printf("Got other key: %s\n", k)
   186  					atomic.StoreInt64(&gotOther, 1)
   187  				}
   188  			}
   189  			return &einsteindbrpc.Response{Resp: &resp}, nil
   190  		},
   191  		onResolveLock: func(req *ekvrpcpb.ResolveLockRequest) (*einsteindbrpc.Response, error) {
   192  			if req.StartVersion != ts {
   193  				return nil, errors.Errorf("Bad start version: %d, expected: %d", req.StartVersion, ts)
   194  			}
   195  			if req.CommitVersion != commitTs {
   196  				return nil, errors.Errorf("Bad commit version: %d, expected: %d", req.CommitVersion, commitTs)
   197  			}
   198  			for _, k := range req.Keys {
   199  				if bytes.Equal(k, []byte("a")) || bytes.Equal(k, []byte("z")) {
   200  					atomic.StoreInt64(&gotResolve, 1)
   201  				} else {
   202  					atomic.StoreInt64(&gotOther, 1)
   203  				}
   204  			}
   205  			resp := ekvrpcpb.ResolveLockResponse{}
   206  			return &einsteindbrpc.Response{Resp: &resp}, nil
   207  		},
   208  	}
   209  	s.causetstore.client = &mock
   210  
   211  	status = TxnStatus{primaryLock: &ekvrpcpb.LockInfo{Secondaries: [][]byte{[]byte("a"), []byte("i")}, UseAsyncCommit: true}}
   212  	dagger = &Lock{
   213  		Key:            []byte("a"),
   214  		Primary:        []byte("z"),
   215  		TxnID:          ts,
   216  		LockType:       ekvrpcpb.Op_Put,
   217  		UseAsyncCommit: true,
   218  		MinCommitTS:    ts + 5,
   219  	}
   220  
   221  	_, err = s.causetstore.Begin()
   222  	c.Assert(err, IsNil)
   223  
   224  	err = s.causetstore.lockResolver.resolveLockAsync(s.bo, dagger, status)
   225  	c.Assert(err, IsNil)
   226  	c.Assert(gotCheckA, Equals, int64(1))
   227  	c.Assert(gotCheckB, Equals, int64(1))
   228  	c.Assert(gotOther, Equals, int64(0))
   229  	c.Assert(gotResolve, Equals, int64(1))
   230  
   231  	// One key has been rolled back (b), one is locked (a). Should be rolled back.
   232  	ts, err = s.causetstore.oracle.GetTimestamp(context.Background())
   233  	c.Assert(err, IsNil)
   234  	commitTs = ts + 10
   235  
   236  	gotCheckA = int64(0)
   237  	gotCheckB = int64(0)
   238  	gotResolve = int64(0)
   239  	gotOther = int64(0)
   240  	mock.onResolveLock = func(req *ekvrpcpb.ResolveLockRequest) (*einsteindbrpc.Response, error) {
   241  		if req.StartVersion != ts {
   242  			return nil, errors.Errorf("Bad start version: %d, expected: %d", req.StartVersion, ts)
   243  		}
   244  		if req.CommitVersion != commitTs {
   245  			return nil, errors.Errorf("Bad commit version: %d, expected: 0", req.CommitVersion)
   246  		}
   247  		for _, k := range req.Keys {
   248  			if bytes.Equal(k, []byte("a")) || bytes.Equal(k, []byte("z")) {
   249  				atomic.StoreInt64(&gotResolve, 1)
   250  			} else {
   251  				atomic.StoreInt64(&gotOther, 1)
   252  			}
   253  		}
   254  		resp := ekvrpcpb.ResolveLockResponse{}
   255  		return &einsteindbrpc.Response{Resp: &resp}, nil
   256  	}
   257  
   258  	dagger.TxnID = ts
   259  	dagger.MinCommitTS = ts + 5
   260  
   261  	err = s.causetstore.lockResolver.resolveLockAsync(s.bo, dagger, status)
   262  	c.Assert(err, IsNil)
   263  	c.Assert(gotCheckA, Equals, int64(1))
   264  	c.Assert(gotCheckB, Equals, int64(1))
   265  	c.Assert(gotResolve, Equals, int64(1))
   266  	c.Assert(gotOther, Equals, int64(0))
   267  }
   268  
   269  type mockResolveClient struct {
   270  	inner              Client
   271  	onResolveLock      func(*ekvrpcpb.ResolveLockRequest) (*einsteindbrpc.Response, error)
   272  	onCheckSecondaries func(*ekvrpcpb.CheckSecondaryLocksRequest) (*einsteindbrpc.Response, error)
   273  }
   274  
   275  func (m *mockResolveClient) SendRequest(ctx context.Context, addr string, req *einsteindbrpc.Request, timeout time.Duration) (*einsteindbrpc.Response, error) {
   276  	// Intercept check secondary locks and resolve dagger messages if the callback is non-nil.
   277  	// If the callback returns (nil, nil), forward to the inner client.
   278  	if cr, ok := req.Req.(*ekvrpcpb.CheckSecondaryLocksRequest); ok && m.onCheckSecondaries != nil {
   279  		result, err := m.onCheckSecondaries(cr)
   280  		if result != nil || err != nil {
   281  			return result, err
   282  		}
   283  	} else if rr, ok := req.Req.(*ekvrpcpb.ResolveLockRequest); ok && m.onResolveLock != nil {
   284  		result, err := m.onResolveLock(rr)
   285  		if result != nil || err != nil {
   286  			return result, err
   287  		}
   288  	}
   289  	return m.inner.SendRequest(ctx, addr, req, timeout)
   290  }
   291  
   292  func (m *mockResolveClient) Close() error {
   293  	return m.inner.Close()
   294  }