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 }