github.com/KinWaiYuen/client-go/v2@v2.5.4/internal/locate/region_cache_test.go (about) 1 // Copyright 2021 TiKV Authors 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 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // NOTE: The code in this file is based on code from the 16 // TiDB project, licensed under the Apache License v 2.0 17 // 18 // https://github.com/pingcap/tidb/tree/cc5e161ac06827589c4966674597c137cc9e809c/store/tikv/locate/region_cache_test.go 19 // 20 21 // Copyright 2016 PingCAP, Inc. 22 // 23 // Licensed under the Apache License, Version 2.0 (the "License"); 24 // you may not use this file except in compliance with the License. 25 // You may obtain a copy of the License at 26 // 27 // http://www.apache.org/licenses/LICENSE-2.0 28 // 29 // Unless required by applicable law or agreed to in writing, software 30 // distributed under the License is distributed on an "AS IS" BASIS, 31 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 32 // See the License for the specific language governing permissions and 33 // limitations under the License. 34 35 package locate 36 37 import ( 38 "context" 39 "errors" 40 "fmt" 41 "math/rand" 42 "testing" 43 "time" 44 45 "github.com/KinWaiYuen/client-go/v2/internal/mockstore/mocktikv" 46 "github.com/KinWaiYuen/client-go/v2/internal/retry" 47 "github.com/KinWaiYuen/client-go/v2/kv" 48 "github.com/google/btree" 49 "github.com/pingcap/kvproto/pkg/errorpb" 50 "github.com/pingcap/kvproto/pkg/metapb" 51 "github.com/stretchr/testify/suite" 52 pd "github.com/tikv/pd/client" 53 ) 54 55 func TestRegionCache(t *testing.T) { 56 suite.Run(t, new(testRegionCacheSuite)) 57 } 58 59 type testRegionCacheSuite struct { 60 suite.Suite 61 mvccStore mocktikv.MVCCStore 62 cluster *mocktikv.Cluster 63 store1 uint64 // store1 is leader 64 store2 uint64 // store2 is follower 65 peer1 uint64 // peer1 is leader 66 peer2 uint64 // peer2 is follower 67 region1 uint64 68 cache *RegionCache 69 bo *retry.Backoffer 70 } 71 72 func (s *testRegionCacheSuite) SetupTest() { 73 s.mvccStore = mocktikv.MustNewMVCCStore() 74 s.cluster = mocktikv.NewCluster(s.mvccStore) 75 storeIDs, peerIDs, regionID, _ := mocktikv.BootstrapWithMultiStores(s.cluster, 2) 76 s.region1 = regionID 77 s.store1 = storeIDs[0] 78 s.store2 = storeIDs[1] 79 s.peer1 = peerIDs[0] 80 s.peer2 = peerIDs[1] 81 pdCli := &CodecPDClient{mocktikv.NewPDClient(s.cluster)} 82 s.cache = NewRegionCache(pdCli) 83 s.bo = retry.NewBackofferWithVars(context.Background(), 5000, nil) 84 } 85 86 func (s *testRegionCacheSuite) TearDownTest() { 87 s.cache.Close() 88 s.mvccStore.Close() 89 } 90 91 func (s *testRegionCacheSuite) storeAddr(id uint64) string { 92 return fmt.Sprintf("store%d", id) 93 } 94 95 func (s *testRegionCacheSuite) checkCache(len int) { 96 ts := time.Now().Unix() 97 s.Equal(validRegions(s.cache.mu.regions, ts), len) 98 s.Equal(validRegionsSearchedByVersions(s.cache.mu.latestVersions, s.cache.mu.regions, ts), len) 99 s.Equal(validRegionsInBtree(s.cache.mu.sorted, ts), len) 100 } 101 102 func validRegionsSearchedByVersions( 103 versions map[uint64]RegionVerID, 104 regions map[RegionVerID]*Region, 105 ts int64, 106 ) (count int) { 107 for _, ver := range versions { 108 region, ok := regions[ver] 109 if !ok || !region.checkRegionCacheTTL(ts) { 110 continue 111 } 112 count++ 113 } 114 return 115 } 116 117 func validRegions(regions map[RegionVerID]*Region, ts int64) (len int) { 118 for _, region := range regions { 119 if !region.checkRegionCacheTTL(ts) { 120 continue 121 } 122 len++ 123 } 124 return 125 } 126 127 func validRegionsInBtree(t *btree.BTree, ts int64) (len int) { 128 t.Descend(func(item btree.Item) bool { 129 r := item.(*btreeItem).cachedRegion 130 if !r.checkRegionCacheTTL(ts) { 131 return true 132 } 133 len++ 134 return true 135 }) 136 return 137 } 138 139 func (s *testRegionCacheSuite) getRegion(key []byte) *Region { 140 _, err := s.cache.LocateKey(s.bo, key) 141 s.Nil(err) 142 r := s.cache.searchCachedRegion(key, false) 143 s.NotNil(r) 144 return r 145 } 146 147 func (s *testRegionCacheSuite) getRegionWithEndKey(key []byte) *Region { 148 _, err := s.cache.LocateEndKey(s.bo, key) 149 s.Nil(err) 150 r := s.cache.searchCachedRegion(key, true) 151 s.NotNil(r) 152 return r 153 } 154 155 func (s *testRegionCacheSuite) getAddr(key []byte, replicaRead kv.ReplicaReadType, seed uint32) string { 156 loc, err := s.cache.LocateKey(s.bo, key) 157 s.Nil(err) 158 ctx, err := s.cache.GetTiKVRPCContext(s.bo, loc.Region, replicaRead, seed) 159 s.Nil(err) 160 if ctx == nil { 161 return "" 162 } 163 return ctx.Addr 164 } 165 166 func (s *testRegionCacheSuite) TestStoreLabels() { 167 testcases := []struct { 168 storeID uint64 169 }{ 170 { 171 storeID: s.store1, 172 }, 173 { 174 storeID: s.store2, 175 }, 176 } 177 for _, testcase := range testcases { 178 s.T().Log(testcase.storeID) 179 store := s.cache.getStoreByStoreID(testcase.storeID) 180 _, err := store.initResolve(s.bo, s.cache) 181 s.Nil(err) 182 labels := []*metapb.StoreLabel{ 183 { 184 Key: "id", 185 Value: fmt.Sprintf("%v", testcase.storeID), 186 }, 187 } 188 stores := s.cache.getStoresByLabels(labels) 189 s.Equal(len(stores), 1) 190 s.Equal(stores[0].labels, labels) 191 } 192 } 193 194 func (s *testRegionCacheSuite) TestSimple() { 195 seed := rand.Uint32() 196 r := s.getRegion([]byte("a")) 197 s.NotNil(r) 198 s.Equal(r.GetID(), s.region1) 199 s.Equal(s.getAddr([]byte("a"), kv.ReplicaReadLeader, 0), s.storeAddr(s.store1)) 200 s.Equal(s.getAddr([]byte("a"), kv.ReplicaReadFollower, seed), s.storeAddr(s.store2)) 201 s.checkCache(1) 202 s.Equal(r.GetMeta(), r.meta) 203 s.Equal(r.GetLeaderPeerID(), r.meta.Peers[r.getStore().workTiKVIdx].Id) 204 s.cache.mu.regions[r.VerID()].lastAccess = 0 205 r = s.cache.searchCachedRegion([]byte("a"), true) 206 s.Nil(r) 207 } 208 209 // TestResolveStateTransition verifies store's resolve state transition. For example, 210 // a newly added store is in unresolved state and will be resolved soon if it's an up store, 211 // or in tombstone state if it's a tombstone. 212 func (s *testRegionCacheSuite) TestResolveStateTransition() { 213 cache := s.cache 214 bo := retry.NewNoopBackoff(context.Background()) 215 216 // Check resolving normal stores. The resolve state should be resolved. 217 for _, storeMeta := range s.cluster.GetAllStores() { 218 store := cache.getStoreByStoreID(storeMeta.GetId()) 219 s.Equal(store.getResolveState(), unresolved) 220 addr, err := store.initResolve(bo, cache) 221 s.Nil(err) 222 s.Equal(addr, storeMeta.GetAddress()) 223 s.Equal(store.getResolveState(), resolved) 224 } 225 226 waitResolve := func(s *Store) { 227 for i := 0; i < 10; i++ { 228 if s.getResolveState() != needCheck { 229 break 230 } 231 time.Sleep(50 * time.Millisecond) 232 } 233 } 234 235 // Mark the store needCheck. The resolve state should be resolved soon. 236 store := cache.getStoreByStoreID(s.store1) 237 store.markNeedCheck(cache.notifyCheckCh) 238 waitResolve(store) 239 s.Equal(store.getResolveState(), resolved) 240 241 // Mark the store needCheck and it becomes a tombstone. The resolve state should be tombstone. 242 s.cluster.MarkTombstone(s.store1) 243 store.markNeedCheck(cache.notifyCheckCh) 244 waitResolve(store) 245 s.Equal(store.getResolveState(), tombstone) 246 s.cluster.StartStore(s.store1) 247 248 // Mark the store needCheck and it's deleted from PD. The resolve state should be tombstone. 249 cache.clear() 250 store = cache.getStoreByStoreID(s.store1) 251 store.initResolve(bo, cache) 252 s.Equal(store.getResolveState(), resolved) 253 storeMeta := s.cluster.GetStore(s.store1) 254 s.cluster.RemoveStore(s.store1) 255 store.markNeedCheck(cache.notifyCheckCh) 256 waitResolve(store) 257 s.Equal(store.getResolveState(), tombstone) 258 s.cluster.AddStore(storeMeta.GetId(), storeMeta.GetAddress(), storeMeta.GetLabels()...) 259 260 // Mark the store needCheck and its address and labels are changed. 261 // The resolve state should be deleted and a new store is added to the cache. 262 cache.clear() 263 store = cache.getStoreByStoreID(s.store1) 264 store.initResolve(bo, cache) 265 s.Equal(store.getResolveState(), resolved) 266 s.cluster.UpdateStoreAddr(s.store1, store.addr+"0", &metapb.StoreLabel{Key: "k", Value: "v"}) 267 store.markNeedCheck(cache.notifyCheckCh) 268 waitResolve(store) 269 s.Equal(store.getResolveState(), deleted) 270 newStore := cache.getStoreByStoreID(s.store1) 271 s.Equal(newStore.getResolveState(), resolved) 272 s.Equal(newStore.addr, store.addr+"0") 273 s.Equal(newStore.labels, []*metapb.StoreLabel{{Key: "k", Value: "v"}}) 274 275 // Check initResolve()ing a tombstone store. The resolve state should be tombstone. 276 cache.clear() 277 s.cluster.MarkTombstone(s.store1) 278 store = cache.getStoreByStoreID(s.store1) 279 for i := 0; i < 2; i++ { 280 addr, err := store.initResolve(bo, cache) 281 s.Nil(err) 282 s.Equal(addr, "") 283 s.Equal(store.getResolveState(), tombstone) 284 } 285 s.cluster.StartStore(s.store1) 286 cache.clear() 287 288 // Check initResolve()ing a dropped store. The resolve state should be tombstone. 289 cache.clear() 290 storeMeta = s.cluster.GetStore(s.store1) 291 s.cluster.RemoveStore(s.store1) 292 store = cache.getStoreByStoreID(s.store1) 293 for i := 0; i < 2; i++ { 294 addr, err := store.initResolve(bo, cache) 295 s.Nil(err) 296 s.Equal(addr, "") 297 s.Equal(store.getResolveState(), tombstone) 298 } 299 s.cluster.AddStore(storeMeta.GetId(), storeMeta.GetAddress(), storeMeta.GetLabels()...) 300 } 301 302 // TestFilterDownPeersOrPeersOnTombstoneOrDroppedStore verifies the RegionCache filter 303 // region's down peers and peers on tombstone or dropped stores. RegionCache shouldn't 304 // report errors in such cases if there are available peers. 305 func (s *testRegionCacheSuite) TestFilterDownPeersOrPeersOnTombstoneOrDroppedStores() { 306 key := []byte("a") 307 bo := retry.NewBackofferWithVars(context.Background(), 100, nil) 308 309 verifyGetRPCCtx := func(meta *metapb.Region) { 310 loc, err := s.cache.LocateKey(bo, key) 311 s.NotNil(loc) 312 s.Nil(err) 313 ctx, err := s.cache.GetTiKVRPCContext(bo, loc.Region, kv.ReplicaReadLeader, 0) 314 s.Nil(err) 315 s.NotNil(ctx) 316 s.Equal(ctx.Meta, meta) 317 ctx, err = s.cache.GetTiKVRPCContext(bo, loc.Region, kv.ReplicaReadFollower, rand.Uint32()) 318 s.Nil(err) 319 s.NotNil(ctx) 320 s.Equal(ctx.Meta, meta) 321 } 322 323 // When all peers are normal, the cached region should contain all peers. 324 reg, err := s.cache.findRegionByKey(bo, key, false) 325 s.NotNil(reg) 326 s.Nil(err) 327 regInPD, _ := s.cluster.GetRegion(reg.GetID()) 328 s.Equal(reg.meta, regInPD) 329 s.Equal(len(reg.meta.GetPeers()), len(reg.getStore().stores)) 330 verifyGetRPCCtx(reg.meta) 331 s.checkCache(1) 332 s.cache.clear() 333 334 // Shouldn't contain the peer on the tombstone store. 335 s.cluster.MarkTombstone(s.store1) 336 reg, err = s.cache.findRegionByKey(bo, key, false) 337 s.NotNil(reg) 338 s.Nil(err) 339 s.Equal(len(reg.meta.GetPeers()), len(regInPD.GetPeers())-1) 340 s.Equal(len(reg.meta.GetPeers()), len(reg.getStore().stores)) 341 for _, peer := range reg.meta.GetPeers() { 342 s.NotEqual(peer.GetStoreId(), s.store1) 343 } 344 for _, store := range reg.getStore().stores { 345 s.NotEqual(store.storeID, s.store1) 346 } 347 verifyGetRPCCtx(reg.meta) 348 s.checkCache(1) 349 s.cache.clear() 350 s.cluster.StartStore(s.store1) 351 352 // Shouldn't contain the peer on the dropped store. 353 store := s.cluster.GetStore(s.store1) 354 s.cluster.RemoveStore(s.store1) 355 reg, err = s.cache.findRegionByKey(bo, key, false) 356 s.NotNil(reg) 357 s.Nil(err) 358 s.Equal(len(reg.meta.GetPeers()), len(regInPD.GetPeers())-1) 359 s.Equal(len(reg.meta.GetPeers()), len(reg.getStore().stores)) 360 for _, peer := range reg.meta.GetPeers() { 361 s.NotEqual(peer.GetStoreId(), s.store1) 362 } 363 for _, store := range reg.getStore().stores { 364 s.NotEqual(store.storeID, s.store1) 365 } 366 verifyGetRPCCtx(reg.meta) 367 s.checkCache(1) 368 s.cache.clear() 369 s.cluster.AddStore(store.GetId(), store.GetAddress(), store.GetLabels()...) 370 371 // Report an error when there's no available peers. 372 s.cluster.MarkTombstone(s.store1) 373 s.cluster.MarkTombstone(s.store2) 374 _, err = s.cache.findRegionByKey(bo, key, false) 375 s.NotNil(err) 376 s.Regexp(".*no available peers.", err.Error()) 377 s.cluster.StartStore(s.store1) 378 s.cluster.StartStore(s.store2) 379 } 380 381 func (s *testRegionCacheSuite) TestUpdateLeader() { 382 seed := rand.Uint32() 383 loc, err := s.cache.LocateKey(s.bo, []byte("a")) 384 s.Nil(err) 385 // tikv-server reports `NotLeader` 386 s.cache.UpdateLeader(loc.Region, &metapb.Peer{Id: s.peer2, StoreId: s.store2}, 0) 387 388 r := s.getRegion([]byte("a")) 389 s.NotNil(r) 390 s.Equal(r.GetID(), s.region1) 391 s.Equal(s.getAddr([]byte("a"), kv.ReplicaReadLeader, 0), s.storeAddr(s.store2)) 392 s.Equal(s.getAddr([]byte("a"), kv.ReplicaReadFollower, seed), s.storeAddr(s.store1)) 393 394 r = s.getRegionWithEndKey([]byte("z")) 395 s.NotNil(r) 396 s.Equal(r.GetID(), s.region1) 397 s.Equal(s.getAddr([]byte("z"), kv.ReplicaReadLeader, 0), s.storeAddr(s.store2)) 398 s.Equal(s.getAddr([]byte("a"), kv.ReplicaReadFollower, seed), s.storeAddr(s.store1)) 399 } 400 401 func (s *testRegionCacheSuite) TestUpdateLeader2() { 402 seed := rand.Uint32() 403 loc, err := s.cache.LocateKey(s.bo, []byte("a")) 404 s.Nil(err) 405 // new store3 becomes leader 406 store3 := s.cluster.AllocID() 407 peer3 := s.cluster.AllocID() 408 s.cluster.AddStore(store3, s.storeAddr(store3)) 409 s.cluster.AddPeer(s.region1, store3, peer3) 410 // tikv-server reports `NotLeader` 411 s.cache.UpdateLeader(loc.Region, &metapb.Peer{Id: peer3, StoreId: store3}, 0) 412 413 // Store3 does not exist in cache, causes a reload from PD. 414 r := s.getRegion([]byte("a")) 415 s.NotNil(r) 416 s.Equal(r.GetID(), s.region1) 417 s.Equal(s.getAddr([]byte("a"), kv.ReplicaReadLeader, 0), s.storeAddr(s.store1)) 418 follower := s.getAddr([]byte("a"), kv.ReplicaReadFollower, seed) 419 if seed%2 == 0 { 420 s.Equal(follower, s.storeAddr(s.store2)) 421 } else { 422 s.Equal(follower, s.storeAddr(store3)) 423 } 424 follower2 := s.getAddr([]byte("a"), kv.ReplicaReadFollower, seed+1) 425 if (seed+1)%2 == 0 { 426 s.Equal(follower2, s.storeAddr(s.store2)) 427 } else { 428 s.Equal(follower2, s.storeAddr(store3)) 429 } 430 s.NotEqual(follower, follower2) 431 432 // tikv-server notifies new leader to pd-server. 433 s.cluster.ChangeLeader(s.region1, peer3) 434 // tikv-server reports `NotLeader` again. 435 s.cache.UpdateLeader(r.VerID(), &metapb.Peer{Id: peer3, StoreId: store3}, 0) 436 r = s.getRegion([]byte("a")) 437 s.NotNil(r) 438 s.Equal(r.GetID(), s.region1) 439 s.Equal(s.getAddr([]byte("a"), kv.ReplicaReadLeader, 0), s.storeAddr(store3)) 440 follower = s.getAddr([]byte("a"), kv.ReplicaReadFollower, seed) 441 if seed%2 == 0 { 442 s.Equal(follower, s.storeAddr(s.store1)) 443 } else { 444 s.Equal(follower, s.storeAddr(s.store2)) 445 } 446 follower2 = s.getAddr([]byte("a"), kv.ReplicaReadFollower, seed+1) 447 if (seed+1)%2 == 0 { 448 s.Equal(follower2, s.storeAddr(s.store1)) 449 } else { 450 s.Equal(follower2, s.storeAddr(s.store2)) 451 } 452 s.NotEqual(follower, follower2) 453 } 454 455 func (s *testRegionCacheSuite) TestUpdateLeader3() { 456 seed := rand.Uint32() 457 loc, err := s.cache.LocateKey(s.bo, []byte("a")) 458 s.Nil(err) 459 // store2 becomes leader 460 s.cluster.ChangeLeader(s.region1, s.peer2) 461 // store2 gone, store3 becomes leader 462 s.cluster.RemoveStore(s.store2) 463 store3 := s.cluster.AllocID() 464 peer3 := s.cluster.AllocID() 465 s.cluster.AddStore(store3, s.storeAddr(store3)) 466 s.cluster.AddPeer(s.region1, store3, peer3) 467 // tikv-server notifies new leader to pd-server. 468 s.cluster.ChangeLeader(s.region1, peer3) 469 // tikv-server reports `NotLeader`(store2 is the leader) 470 s.cache.UpdateLeader(loc.Region, &metapb.Peer{Id: s.peer2, StoreId: s.store2}, 0) 471 472 // Store2 does not exist any more, causes a reload from PD. 473 r := s.getRegion([]byte("a")) 474 s.Nil(err) 475 s.NotNil(r) 476 s.Equal(r.GetID(), s.region1) 477 loc, err = s.cache.LocateKey(s.bo, []byte("a")) 478 s.Nil(err) 479 // return resolved store2 address and send fail 480 ctx, err := s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadLeader, seed) 481 s.Nil(err) 482 s.Equal(ctx.Addr, "store2") 483 s.cache.OnSendFail(retry.NewNoopBackoff(context.Background()), ctx, false, errors.New("send fail")) 484 s.cache.checkAndResolve(nil, func(*Store) bool { return true }) 485 s.cache.UpdateLeader(loc.Region, &metapb.Peer{Id: s.peer2, StoreId: s.store2}, 0) 486 addr := s.getAddr([]byte("a"), kv.ReplicaReadLeader, 0) 487 s.Equal(addr, "") 488 addr = s.getAddr([]byte("a"), kv.ReplicaReadLeader, 0) 489 s.Equal(addr, s.storeAddr(store3)) 490 491 addr = s.getAddr([]byte("a"), kv.ReplicaReadFollower, seed) 492 addr2 := s.getAddr([]byte("a"), kv.ReplicaReadFollower, seed+1) 493 s.NotEqual(addr, s.storeAddr(store3)) 494 s.NotEqual(addr2, s.storeAddr(store3)) 495 } 496 497 func (s *testRegionCacheSuite) TestSendFailedButLeaderNotChange() { 498 // 3 nodes and no.1 is leader. 499 store3 := s.cluster.AllocID() 500 peer3 := s.cluster.AllocID() 501 s.cluster.AddStore(store3, s.storeAddr(store3)) 502 s.cluster.AddPeer(s.region1, store3, peer3) 503 s.cluster.ChangeLeader(s.region1, s.peer1) 504 505 loc, err := s.cache.LocateKey(s.bo, []byte("a")) 506 s.Nil(err) 507 ctx, err := s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadLeader, 0) 508 s.Nil(err) 509 s.Equal(ctx.Peer.Id, s.peer1) 510 s.Equal(len(ctx.Meta.Peers), 3) 511 512 // verify follower to be one of store2 and store3 513 seed := rand.Uint32() 514 ctxFollower1, err := s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadFollower, seed) 515 s.Nil(err) 516 if seed%2 == 0 { 517 s.Equal(ctxFollower1.Peer.Id, s.peer2) 518 } else { 519 s.Equal(ctxFollower1.Peer.Id, peer3) 520 } 521 ctxFollower2, err := s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadFollower, seed) 522 s.Nil(err) 523 if seed%2 == 0 { 524 s.Equal(ctxFollower2.Peer.Id, s.peer2) 525 } else { 526 s.Equal(ctxFollower2.Peer.Id, peer3) 527 } 528 s.Equal(ctxFollower1.Peer.Id, ctxFollower2.Peer.Id) 529 530 // send fail leader switch to 2 531 s.cache.OnSendFail(s.bo, ctx, false, nil) 532 ctx, err = s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadLeader, 0) 533 s.Nil(err) 534 s.Equal(ctx.Peer.Id, s.peer2) 535 536 // verify follower to be one of store1 and store3 537 ctxFollower1, err = s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadFollower, seed) 538 s.Nil(err) 539 if seed%2 == 0 { 540 s.Equal(ctxFollower1.Peer.Id, s.peer1) 541 } else { 542 s.Equal(ctxFollower1.Peer.Id, peer3) 543 } 544 ctxFollower2, err = s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadFollower, seed+1) 545 s.Nil(err) 546 if (seed+1)%2 == 0 { 547 s.Equal(ctxFollower2.Peer.Id, s.peer1) 548 } else { 549 s.Equal(ctxFollower2.Peer.Id, peer3) 550 } 551 s.NotEqual(ctxFollower1.Peer.Id, ctxFollower2.Peer.Id) 552 553 // access 1 it will return NotLeader, leader back to 2 again 554 s.cache.UpdateLeader(loc.Region, &metapb.Peer{Id: s.peer2, StoreId: s.store2}, ctx.AccessIdx) 555 ctx, err = s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadLeader, 0) 556 s.Nil(err) 557 s.Equal(ctx.Peer.Id, s.peer2) 558 559 // verify follower to be one of store1 and store3 560 ctxFollower1, err = s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadFollower, seed) 561 s.Nil(err) 562 if seed%2 == 0 { 563 s.Equal(ctxFollower1.Peer.Id, s.peer1) 564 } else { 565 s.Equal(ctxFollower1.Peer.Id, peer3) 566 } 567 ctxFollower2, err = s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadFollower, seed+1) 568 s.Nil(err) 569 if (seed+1)%2 == 0 { 570 s.Equal(ctxFollower2.Peer.Id, s.peer1) 571 } else { 572 s.Equal(ctxFollower2.Peer.Id, peer3) 573 } 574 s.NotEqual(ctxFollower1.Peer.Id, ctxFollower2.Peer.Id) 575 } 576 577 func (s *testRegionCacheSuite) TestSendFailedInHibernateRegion() { 578 // 3 nodes and no.1 is leader. 579 store3 := s.cluster.AllocID() 580 peer3 := s.cluster.AllocID() 581 s.cluster.AddStore(store3, s.storeAddr(store3)) 582 s.cluster.AddPeer(s.region1, store3, peer3) 583 s.cluster.ChangeLeader(s.region1, s.peer1) 584 585 loc, err := s.cache.LocateKey(s.bo, []byte("a")) 586 s.Nil(err) 587 ctx, err := s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadLeader, 0) 588 s.Nil(err) 589 s.Equal(ctx.Peer.Id, s.peer1) 590 s.Equal(len(ctx.Meta.Peers), 3) 591 592 // verify follower to be one of store2 and store3 593 seed := rand.Uint32() 594 ctxFollower1, err := s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadFollower, seed) 595 s.Nil(err) 596 if seed%2 == 0 { 597 s.Equal(ctxFollower1.Peer.Id, s.peer2) 598 } else { 599 s.Equal(ctxFollower1.Peer.Id, peer3) 600 } 601 ctxFollower2, err := s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadFollower, seed) 602 s.Nil(err) 603 if seed%2 == 0 { 604 s.Equal(ctxFollower2.Peer.Id, s.peer2) 605 } else { 606 s.Equal(ctxFollower2.Peer.Id, peer3) 607 } 608 s.Equal(ctxFollower1.Peer.Id, ctxFollower2.Peer.Id) 609 610 // send fail leader switch to 2 611 s.cache.OnSendFail(s.bo, ctx, false, nil) 612 ctx, err = s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadLeader, 0) 613 s.Nil(err) 614 s.Equal(ctx.Peer.Id, s.peer2) 615 616 // verify follower to be one of store1 and store3 617 ctxFollower1, err = s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadFollower, seed) 618 s.Nil(err) 619 if seed%2 == 0 { 620 s.Equal(ctxFollower1.Peer.Id, s.peer1) 621 } else { 622 s.Equal(ctxFollower1.Peer.Id, peer3) 623 } 624 s.True(ctxFollower1.Peer.Id == s.peer1 || ctxFollower1.Peer.Id == peer3) 625 ctxFollower2, err = s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadFollower, seed+1) 626 s.Nil(err) 627 if (seed+1)%2 == 0 { 628 s.Equal(ctxFollower2.Peer.Id, s.peer1) 629 } else { 630 s.Equal(ctxFollower2.Peer.Id, peer3) 631 } 632 s.NotEqual(ctxFollower1.Peer.Id, ctxFollower2.Peer.Id) 633 634 // access 2, it's in hibernate and return 0 leader, so switch to 3 635 s.cache.UpdateLeader(loc.Region, nil, ctx.AccessIdx) 636 ctx, err = s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadLeader, 0) 637 s.Nil(err) 638 s.Equal(ctx.Peer.Id, peer3) 639 640 // verify follower to be one of store1 and store2 641 ctxFollower1, err = s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadFollower, seed) 642 s.Nil(err) 643 if seed%2 == 0 { 644 s.Equal(ctxFollower1.Peer.Id, s.peer1) 645 } else { 646 s.Equal(ctxFollower1.Peer.Id, s.peer2) 647 } 648 ctxFollower2, err = s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadFollower, seed) 649 s.Nil(err) 650 if seed%2 == 0 { 651 s.Equal(ctxFollower2.Peer.Id, s.peer1) 652 } else { 653 s.Equal(ctxFollower2.Peer.Id, s.peer2) 654 } 655 s.Equal(ctxFollower1.Peer.Id, ctxFollower2.Peer.Id) 656 657 // again peer back to 1 658 ctx, err = s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadLeader, 0) 659 s.Nil(err) 660 s.cache.UpdateLeader(loc.Region, nil, ctx.AccessIdx) 661 ctx, err = s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadLeader, 0) 662 s.Nil(err) 663 s.Equal(ctx.Peer.Id, s.peer1) 664 665 // verify follower to be one of store2 and store3 666 ctxFollower1, err = s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadFollower, seed) 667 s.Nil(err) 668 if seed%2 == 0 { 669 s.Equal(ctxFollower1.Peer.Id, s.peer2) 670 } else { 671 s.Equal(ctxFollower1.Peer.Id, peer3) 672 } 673 ctxFollower2, err = s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadFollower, seed+1) 674 s.Nil(err) 675 if (seed+1)%2 == 0 { 676 s.Equal(ctxFollower2.Peer.Id, s.peer2) 677 } else { 678 s.Equal(ctxFollower2.Peer.Id, peer3) 679 } 680 s.NotEqual(ctxFollower1.Peer.Id, ctxFollower2.Peer.Id) 681 } 682 683 func (s *testRegionCacheSuite) TestSendFailInvalidateRegionsInSameStore() { 684 // key range: ['' - 'm' - 'z'] 685 region2 := s.cluster.AllocID() 686 newPeers := s.cluster.AllocIDs(2) 687 s.cluster.Split(s.region1, region2, []byte("m"), newPeers, newPeers[0]) 688 689 // Check the two regions. 690 loc1, err := s.cache.LocateKey(s.bo, []byte("a")) 691 s.Nil(err) 692 s.Equal(loc1.Region.id, s.region1) 693 loc2, err := s.cache.LocateKey(s.bo, []byte("x")) 694 s.Nil(err) 695 s.Equal(loc2.Region.id, region2) 696 697 // Send fail on region1 698 ctx, _ := s.cache.GetTiKVRPCContext(s.bo, loc1.Region, kv.ReplicaReadLeader, 0) 699 s.checkCache(2) 700 s.cache.OnSendFail(s.bo, ctx, false, errors.New("test error")) 701 702 // Get region2 cache will get nil then reload. 703 ctx2, err := s.cache.GetTiKVRPCContext(s.bo, loc2.Region, kv.ReplicaReadLeader, 0) 704 s.Nil(ctx2) 705 s.Nil(err) 706 } 707 708 func (s *testRegionCacheSuite) TestSendFailedInMultipleNode() { 709 // 3 nodes and no.1 is leader. 710 store3 := s.cluster.AllocID() 711 peer3 := s.cluster.AllocID() 712 s.cluster.AddStore(store3, s.storeAddr(store3)) 713 s.cluster.AddPeer(s.region1, store3, peer3) 714 s.cluster.ChangeLeader(s.region1, s.peer1) 715 716 loc, err := s.cache.LocateKey(s.bo, []byte("a")) 717 s.Nil(err) 718 ctx, err := s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadLeader, 0) 719 s.Nil(err) 720 s.Equal(ctx.Peer.Id, s.peer1) 721 s.Equal(len(ctx.Meta.Peers), 3) 722 723 // verify follower to be one of store2 and store3 724 seed := rand.Uint32() 725 ctxFollower1, err := s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadFollower, seed) 726 s.Nil(err) 727 if seed%2 == 0 { 728 s.Equal(ctxFollower1.Peer.Id, s.peer2) 729 } else { 730 s.Equal(ctxFollower1.Peer.Id, peer3) 731 } 732 ctxFollower2, err := s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadFollower, seed) 733 s.Nil(err) 734 if seed%2 == 0 { 735 s.Equal(ctxFollower2.Peer.Id, s.peer2) 736 } else { 737 s.Equal(ctxFollower2.Peer.Id, peer3) 738 } 739 s.Equal(ctxFollower1.Peer.Id, ctxFollower2.Peer.Id) 740 741 // send fail leader switch to 2 742 s.cache.OnSendFail(s.bo, ctx, false, nil) 743 ctx, err = s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadLeader, 0) 744 s.Nil(err) 745 s.Equal(ctx.Peer.Id, s.peer2) 746 747 // verify follower to be one of store1 and store3 748 ctxFollower1, err = s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadFollower, seed) 749 s.Nil(err) 750 if seed%2 == 0 { 751 s.Equal(ctxFollower1.Peer.Id, s.peer1) 752 } else { 753 s.Equal(ctxFollower1.Peer.Id, peer3) 754 } 755 ctxFollower2, err = s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadFollower, seed+1) 756 s.Nil(err) 757 if (seed+1)%2 == 0 { 758 s.Equal(ctxFollower2.Peer.Id, s.peer1) 759 } else { 760 s.Equal(ctxFollower2.Peer.Id, peer3) 761 } 762 s.NotEqual(ctxFollower1.Peer.Id, ctxFollower2.Peer.Id) 763 764 // send 2 fail leader switch to 3 765 s.cache.OnSendFail(s.bo, ctx, false, nil) 766 ctx, err = s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadLeader, 0) 767 s.Nil(err) 768 s.Equal(ctx.Peer.Id, peer3) 769 770 // verify follower to be one of store1 and store2 771 ctxFollower1, err = s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadFollower, seed) 772 s.Nil(err) 773 if seed%2 == 0 { 774 s.Equal(ctxFollower1.Peer.Id, s.peer1) 775 } else { 776 s.Equal(ctxFollower1.Peer.Id, s.peer2) 777 } 778 s.True(ctxFollower1.Peer.Id == s.peer1 || ctxFollower1.Peer.Id == s.peer2) 779 ctxFollower2, err = s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadFollower, seed) 780 s.Nil(err) 781 if seed%2 == 0 { 782 s.Equal(ctxFollower2.Peer.Id, s.peer1) 783 } else { 784 s.Equal(ctxFollower2.Peer.Id, s.peer2) 785 } 786 s.Equal(ctxFollower1.Peer.Id, ctxFollower2.Peer.Id) 787 788 // 3 can be access, so switch to 1 789 s.cache.UpdateLeader(loc.Region, &metapb.Peer{Id: s.peer1, StoreId: s.store1}, ctx.AccessIdx) 790 ctx, err = s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadLeader, 0) 791 s.Nil(err) 792 s.Equal(ctx.Peer.Id, s.peer1) 793 794 // verify follower to be one of store2 and store3 795 ctxFollower1, err = s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadFollower, seed) 796 s.Nil(err) 797 if seed%2 == 0 { 798 s.Equal(ctxFollower1.Peer.Id, s.peer2) 799 } else { 800 s.Equal(ctxFollower1.Peer.Id, peer3) 801 } 802 ctxFollower2, err = s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadFollower, seed+1) 803 s.Nil(err) 804 if (seed+1)%2 == 0 { 805 s.Equal(ctxFollower2.Peer.Id, s.peer2) 806 } else { 807 s.Equal(ctxFollower2.Peer.Id, peer3) 808 } 809 s.NotEqual(ctxFollower1.Peer.Id, ctxFollower2.Peer.Id) 810 } 811 812 func (s *testRegionCacheSuite) TestLabelSelectorTiKVPeer() { 813 dc1Label := []*metapb.StoreLabel{ 814 { 815 Key: "zone", 816 Value: "dc-1", 817 }, 818 } 819 dc2Label := []*metapb.StoreLabel{ 820 { 821 Key: "zone", 822 Value: "dc-2", 823 }, 824 } 825 dc3Label := []*metapb.StoreLabel{ 826 { 827 Key: "zone", 828 Value: "dc-3", 829 }, 830 } 831 s.cluster.UpdateStoreLabels(s.store1, dc1Label) 832 s.cluster.UpdateStoreLabels(s.store2, dc2Label) 833 834 store3 := s.cluster.AllocID() 835 peer3 := s.cluster.AllocID() 836 s.cluster.AddStore(store3, s.storeAddr(store3)) 837 s.cluster.AddPeer(s.region1, store3, peer3) 838 s.cluster.UpdateStoreLabels(store3, dc1Label) 839 // Region have 3 peer, leader located in dc-1, followers located in dc-1, dc-2 840 loc, err := s.cache.LocateKey(s.bo, []byte("a")) 841 s.Nil(err) 842 seed := rand.Uint32() 843 844 testcases := []struct { 845 name string 846 t kv.ReplicaReadType 847 labels []*metapb.StoreLabel 848 expectStoreIDRange map[uint64]struct{} 849 }{ 850 { 851 name: "any Peer,located in dc-1", 852 t: kv.ReplicaReadMixed, 853 labels: dc1Label, 854 expectStoreIDRange: map[uint64]struct{}{ 855 s.store1: {}, 856 store3: {}, 857 }, 858 }, 859 { 860 name: "any Peer,located in dc-2", 861 t: kv.ReplicaReadMixed, 862 labels: dc2Label, 863 expectStoreIDRange: map[uint64]struct{}{ 864 s.store2: {}, 865 }, 866 }, 867 { 868 name: "only follower,located in dc-1", 869 t: kv.ReplicaReadFollower, 870 labels: dc1Label, 871 expectStoreIDRange: map[uint64]struct{}{ 872 store3: {}, 873 }, 874 }, 875 { 876 name: "only leader, shouldn't consider labels", 877 t: kv.ReplicaReadLeader, 878 labels: dc2Label, 879 expectStoreIDRange: map[uint64]struct{}{ 880 s.store1: {}, 881 }, 882 }, 883 { 884 name: "no label matching, fallback to leader", 885 t: kv.ReplicaReadMixed, 886 labels: dc3Label, 887 expectStoreIDRange: map[uint64]struct{}{ 888 s.store1: {}, 889 }, 890 }, 891 } 892 893 for _, testcase := range testcases { 894 s.T().Log(testcase.name) 895 ctx, err := s.cache.GetTiKVRPCContext(s.bo, loc.Region, testcase.t, seed, WithMatchLabels(testcase.labels)) 896 s.Nil(err) 897 _, exist := testcase.expectStoreIDRange[ctx.Store.storeID] 898 s.Equal(exist, true) 899 } 900 } 901 902 func (s *testRegionCacheSuite) TestSplit() { 903 seed := rand.Uint32() 904 r := s.getRegion([]byte("x")) 905 s.Equal(r.GetID(), s.region1) 906 s.Equal(s.getAddr([]byte("x"), kv.ReplicaReadLeader, 0), s.storeAddr(s.store1)) 907 s.Equal(s.getAddr([]byte("x"), kv.ReplicaReadFollower, seed), s.storeAddr(s.store2)) 908 909 // split to ['' - 'm' - 'z'] 910 region2 := s.cluster.AllocID() 911 newPeers := s.cluster.AllocIDs(2) 912 s.cluster.Split(s.region1, region2, []byte("m"), newPeers, newPeers[0]) 913 914 // tikv-server reports `NotInRegion` 915 s.cache.InvalidateCachedRegion(r.VerID()) 916 s.checkCache(0) 917 918 r = s.getRegion([]byte("x")) 919 s.Equal(r.GetID(), region2) 920 s.Equal(s.getAddr([]byte("x"), kv.ReplicaReadLeader, 0), s.storeAddr(s.store1)) 921 s.Equal(s.getAddr([]byte("x"), kv.ReplicaReadFollower, seed), s.storeAddr(s.store2)) 922 s.checkCache(1) 923 924 r = s.getRegionWithEndKey([]byte("m")) 925 s.Equal(r.GetID(), s.region1) 926 s.checkCache(2) 927 } 928 929 func (s *testRegionCacheSuite) TestMerge() { 930 // key range: ['' - 'm' - 'z'] 931 region2 := s.cluster.AllocID() 932 newPeers := s.cluster.AllocIDs(2) 933 s.cluster.Split(s.region1, region2, []byte("m"), newPeers, newPeers[0]) 934 935 loc, err := s.cache.LocateKey(s.bo, []byte("x")) 936 s.Nil(err) 937 s.Equal(loc.Region.id, region2) 938 939 // merge to single region 940 s.cluster.Merge(s.region1, region2) 941 942 // tikv-server reports `NotInRegion` 943 s.cache.InvalidateCachedRegion(loc.Region) 944 s.checkCache(0) 945 946 loc, err = s.cache.LocateKey(s.bo, []byte("x")) 947 s.Nil(err) 948 s.Equal(loc.Region.id, s.region1) 949 s.checkCache(1) 950 } 951 952 func (s *testRegionCacheSuite) TestReconnect() { 953 seed := rand.Uint32() 954 loc, err := s.cache.LocateKey(s.bo, []byte("a")) 955 s.Nil(err) 956 957 // connect tikv-server failed, cause drop cache 958 s.cache.InvalidateCachedRegion(loc.Region) 959 960 r := s.getRegion([]byte("a")) 961 s.NotNil(r) 962 s.Equal(r.GetID(), s.region1) 963 s.Equal(s.getAddr([]byte("a"), kv.ReplicaReadLeader, 0), s.storeAddr(s.store1)) 964 s.Equal(s.getAddr([]byte("x"), kv.ReplicaReadFollower, seed), s.storeAddr(s.store2)) 965 s.checkCache(1) 966 } 967 968 func (s *testRegionCacheSuite) TestRegionEpochAheadOfTiKV() { 969 // Create a separated region cache to do this test. 970 pdCli := &CodecPDClient{mocktikv.NewPDClient(s.cluster)} 971 cache := NewRegionCache(pdCli) 972 defer cache.Close() 973 974 region := createSampleRegion([]byte("k1"), []byte("k2")) 975 region.meta.Id = 1 976 region.meta.RegionEpoch = &metapb.RegionEpoch{Version: 10, ConfVer: 10} 977 cache.insertRegionToCache(region) 978 979 r1 := metapb.Region{Id: 1, RegionEpoch: &metapb.RegionEpoch{Version: 9, ConfVer: 10}} 980 r2 := metapb.Region{Id: 1, RegionEpoch: &metapb.RegionEpoch{Version: 10, ConfVer: 9}} 981 982 bo := retry.NewBackofferWithVars(context.Background(), 2000000, nil) 983 984 _, err := cache.OnRegionEpochNotMatch(bo, &RPCContext{Region: region.VerID()}, []*metapb.Region{&r1}) 985 s.Nil(err) 986 _, err = cache.OnRegionEpochNotMatch(bo, &RPCContext{Region: region.VerID()}, []*metapb.Region{&r2}) 987 s.Nil(err) 988 s.Equal(bo.ErrorsNum(), 2) 989 } 990 991 func (s *testRegionCacheSuite) TestRegionEpochOnTiFlash() { 992 // add store3 as tiflash 993 store3 := s.cluster.AllocID() 994 peer3 := s.cluster.AllocID() 995 s.cluster.UpdateStoreAddr(s.store1, s.storeAddr(s.store1), &metapb.StoreLabel{Key: "engine", Value: "tiflash"}) 996 s.cluster.AddStore(store3, s.storeAddr(store3)) 997 s.cluster.AddPeer(s.region1, store3, peer3) 998 s.cluster.ChangeLeader(s.region1, peer3) 999 1000 // pre-load region cache 1001 loc1, err := s.cache.LocateKey(s.bo, []byte("a")) 1002 s.Nil(err) 1003 s.Equal(loc1.Region.id, s.region1) 1004 lctx, err := s.cache.GetTiKVRPCContext(s.bo, loc1.Region, kv.ReplicaReadLeader, 0) 1005 s.Nil(err) 1006 s.Equal(lctx.Peer.Id, peer3) 1007 1008 // epoch-not-match on tiflash 1009 ctxTiFlash, err := s.cache.GetTiFlashRPCContext(s.bo, loc1.Region, true) 1010 s.Nil(err) 1011 s.Equal(ctxTiFlash.Peer.Id, s.peer1) 1012 ctxTiFlash.Peer.Role = metapb.PeerRole_Learner 1013 r := ctxTiFlash.Meta 1014 reqSend := NewRegionRequestSender(s.cache, nil) 1015 regionErr := &errorpb.Error{EpochNotMatch: &errorpb.EpochNotMatch{CurrentRegions: []*metapb.Region{r}}} 1016 reqSend.onRegionError(s.bo, ctxTiFlash, nil, regionErr) 1017 1018 // check leader read should not go to tiflash 1019 lctx, err = s.cache.GetTiKVRPCContext(s.bo, loc1.Region, kv.ReplicaReadLeader, 0) 1020 s.Nil(err) 1021 s.NotEqual(lctx.Peer.Id, s.peer1) 1022 } 1023 1024 const regionSplitKeyFormat = "t%08d" 1025 1026 func createClusterWithStoresAndRegions(regionCnt, storeCount int) *mocktikv.Cluster { 1027 cluster := mocktikv.NewCluster(mocktikv.MustNewMVCCStore()) 1028 _, _, regionID, _ := mocktikv.BootstrapWithMultiStores(cluster, storeCount) 1029 for i := 0; i < regionCnt; i++ { 1030 rawKey := []byte(fmt.Sprintf(regionSplitKeyFormat, i)) 1031 ids := cluster.AllocIDs(4) 1032 // Make leaders equally distributed on the 3 stores. 1033 storeID := ids[0] 1034 peerIDs := ids[1:] 1035 leaderPeerID := peerIDs[i%3] 1036 cluster.SplitRaw(regionID, storeID, rawKey, peerIDs, leaderPeerID) 1037 regionID = ids[0] 1038 } 1039 return cluster 1040 } 1041 1042 func loadRegionsToCache(cache *RegionCache, regionCnt int) { 1043 for i := 0; i < regionCnt; i++ { 1044 rawKey := []byte(fmt.Sprintf(regionSplitKeyFormat, i)) 1045 cache.LocateKey(retry.NewBackofferWithVars(context.Background(), 1, nil), rawKey) 1046 } 1047 } 1048 1049 func (s *testRegionCacheSuite) TestListRegionIDsInCache() { 1050 // ['' - 'm' - 'z'] 1051 region2 := s.cluster.AllocID() 1052 newPeers := s.cluster.AllocIDs(2) 1053 s.cluster.Split(s.region1, region2, []byte("m"), newPeers, newPeers[0]) 1054 1055 regionIDs, err := s.cache.ListRegionIDsInKeyRange(s.bo, []byte("a"), []byte("z")) 1056 s.Nil(err) 1057 s.Equal(regionIDs, []uint64{s.region1, region2}) 1058 regionIDs, err = s.cache.ListRegionIDsInKeyRange(s.bo, []byte("m"), []byte("z")) 1059 s.Nil(err) 1060 s.Equal(regionIDs, []uint64{region2}) 1061 1062 regionIDs, err = s.cache.ListRegionIDsInKeyRange(s.bo, []byte("a"), []byte("m")) 1063 s.Nil(err) 1064 s.Equal(regionIDs, []uint64{s.region1, region2}) 1065 } 1066 1067 func (s *testRegionCacheSuite) TestScanRegions() { 1068 // Split at "a", "b", "c", "d" 1069 regions := s.cluster.AllocIDs(4) 1070 regions = append([]uint64{s.region1}, regions...) 1071 1072 peers := [][]uint64{{s.peer1, s.peer2}} 1073 for i := 0; i < 4; i++ { 1074 peers = append(peers, s.cluster.AllocIDs(2)) 1075 } 1076 1077 for i := 0; i < 4; i++ { 1078 s.cluster.Split(regions[i], regions[i+1], []byte{'a' + byte(i)}, peers[i+1], peers[i+1][0]) 1079 } 1080 1081 scannedRegions, err := s.cache.scanRegions(s.bo, []byte(""), nil, 100) 1082 s.Nil(err) 1083 s.Equal(len(scannedRegions), 5) 1084 for i := 0; i < 5; i++ { 1085 r := scannedRegions[i] 1086 _, p, _, _ := r.WorkStorePeer(r.getStore()) 1087 1088 s.Equal(r.meta.Id, regions[i]) 1089 s.Equal(p.Id, peers[i][0]) 1090 } 1091 1092 scannedRegions, err = s.cache.scanRegions(s.bo, []byte("a"), nil, 3) 1093 s.Nil(err) 1094 s.Equal(len(scannedRegions), 3) 1095 for i := 1; i < 4; i++ { 1096 r := scannedRegions[i-1] 1097 _, p, _, _ := r.WorkStorePeer(r.getStore()) 1098 1099 s.Equal(r.meta.Id, regions[i]) 1100 s.Equal(p.Id, peers[i][0]) 1101 } 1102 1103 scannedRegions, err = s.cache.scanRegions(s.bo, []byte("a1"), nil, 1) 1104 s.Nil(err) 1105 s.Equal(len(scannedRegions), 1) 1106 1107 r0 := scannedRegions[0] 1108 _, p0, _, _ := r0.WorkStorePeer(r0.getStore()) 1109 s.Equal(r0.meta.Id, regions[1]) 1110 s.Equal(p0.Id, peers[1][0]) 1111 1112 // Test region with no leader 1113 s.cluster.GiveUpLeader(regions[1]) 1114 s.cluster.GiveUpLeader(regions[3]) 1115 scannedRegions, err = s.cache.scanRegions(s.bo, []byte(""), nil, 5) 1116 s.Nil(err) 1117 for i := 0; i < 3; i++ { 1118 r := scannedRegions[i] 1119 _, p, _, _ := r.WorkStorePeer(r.getStore()) 1120 1121 s.Equal(r.meta.Id, regions[i*2]) 1122 s.Equal(p.Id, peers[i*2][0]) 1123 } 1124 } 1125 1126 func (s *testRegionCacheSuite) TestBatchLoadRegions() { 1127 // Split at "a", "b", "c", "d" 1128 regions := s.cluster.AllocIDs(4) 1129 regions = append([]uint64{s.region1}, regions...) 1130 1131 peers := [][]uint64{{s.peer1, s.peer2}} 1132 for i := 0; i < 4; i++ { 1133 peers = append(peers, s.cluster.AllocIDs(2)) 1134 } 1135 1136 for i := 0; i < 4; i++ { 1137 s.cluster.Split(regions[i], regions[i+1], []byte{'a' + byte(i)}, peers[i+1], peers[i+1][0]) 1138 } 1139 1140 testCases := []struct { 1141 startKey []byte 1142 endKey []byte 1143 limit int 1144 expectKey []byte 1145 expectRegions []uint64 1146 }{ 1147 {[]byte(""), []byte("a"), 1, []byte("a"), []uint64{regions[0]}}, 1148 {[]byte("a"), []byte("b1"), 2, []byte("c"), []uint64{regions[1], regions[2]}}, 1149 {[]byte("a1"), []byte("d"), 2, []byte("c"), []uint64{regions[1], regions[2]}}, 1150 {[]byte("c"), []byte("c1"), 2, nil, []uint64{regions[3]}}, 1151 {[]byte("d"), nil, 2, nil, []uint64{regions[4]}}, 1152 } 1153 1154 for _, tc := range testCases { 1155 key, err := s.cache.BatchLoadRegionsFromKey(s.bo, tc.startKey, tc.limit) 1156 s.Nil(err) 1157 if tc.expectKey != nil { 1158 s.Equal(key, tc.expectKey) 1159 } else { 1160 s.Len(key, 0) 1161 } 1162 loadRegions, err := s.cache.BatchLoadRegionsWithKeyRange(s.bo, tc.startKey, tc.endKey, tc.limit) 1163 s.Nil(err) 1164 s.Len(loadRegions, len(tc.expectRegions)) 1165 for i := range loadRegions { 1166 s.Equal(loadRegions[i].GetID(), tc.expectRegions[i]) 1167 } 1168 } 1169 1170 s.checkCache(len(regions)) 1171 } 1172 1173 func (s *testRegionCacheSuite) TestFollowerReadFallback() { 1174 // 3 nodes and no.1 is leader. 1175 store3 := s.cluster.AllocID() 1176 peer3 := s.cluster.AllocID() 1177 s.cluster.AddStore(store3, s.storeAddr(store3)) 1178 s.cluster.AddPeer(s.region1, store3, peer3) 1179 s.cluster.ChangeLeader(s.region1, s.peer1) 1180 1181 loc, err := s.cache.LocateKey(s.bo, []byte("a")) 1182 s.Nil(err) 1183 ctx, err := s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadLeader, 0) 1184 s.Nil(err) 1185 s.Equal(ctx.Peer.Id, s.peer1) 1186 s.Equal(len(ctx.Meta.Peers), 3) 1187 1188 // verify follower to be store2 and store3 1189 ctxFollower1, err := s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadFollower, 0) 1190 s.Nil(err) 1191 s.Equal(ctxFollower1.Peer.Id, s.peer2) 1192 ctxFollower2, err := s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadFollower, 1) 1193 s.Nil(err) 1194 s.Equal(ctxFollower2.Peer.Id, peer3) 1195 s.NotEqual(ctxFollower1.Peer.Id, ctxFollower2.Peer.Id) 1196 1197 // send fail on store2, next follower read is going to fallback to store3 1198 s.cache.OnSendFail(s.bo, ctxFollower1, false, errors.New("test error")) 1199 ctx, err = s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadFollower, 0) 1200 s.Nil(err) 1201 s.Equal(ctx.Peer.Id, peer3) 1202 } 1203 1204 func (s *testRegionCacheSuite) TestMixedReadFallback() { 1205 // 3 nodes and no.1 is leader. 1206 store3 := s.cluster.AllocID() 1207 peer3 := s.cluster.AllocID() 1208 s.cluster.AddStore(store3, s.storeAddr(store3)) 1209 s.cluster.AddPeer(s.region1, store3, peer3) 1210 s.cluster.ChangeLeader(s.region1, s.peer1) 1211 1212 loc, err := s.cache.LocateKey(s.bo, []byte("a")) 1213 s.Nil(err) 1214 ctx, err := s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadLeader, 0) 1215 s.Nil(err) 1216 s.Equal(ctx.Peer.Id, s.peer1) 1217 s.Equal(len(ctx.Meta.Peers), 3) 1218 1219 // verify follower to be store1, store2 and store3 1220 ctxFollower1, err := s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadMixed, 0) 1221 s.Nil(err) 1222 s.Equal(ctxFollower1.Peer.Id, s.peer1) 1223 1224 ctxFollower2, err := s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadMixed, 1) 1225 s.Nil(err) 1226 s.Equal(ctxFollower2.Peer.Id, s.peer2) 1227 1228 ctxFollower3, err := s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadMixed, 2) 1229 s.Nil(err) 1230 s.Equal(ctxFollower3.Peer.Id, peer3) 1231 1232 // send fail on store2, next follower read is going to fallback to store3 1233 s.cache.OnSendFail(s.bo, ctxFollower1, false, errors.New("test error")) 1234 ctx, err = s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadMixed, 0) 1235 s.Nil(err) 1236 s.Equal(ctx.Peer.Id, s.peer2) 1237 } 1238 1239 func (s *testRegionCacheSuite) TestPeersLenChange() { 1240 // 2 peers [peer1, peer2] and let peer2 become leader 1241 loc, err := s.cache.LocateKey(s.bo, []byte("a")) 1242 s.Nil(err) 1243 s.cache.UpdateLeader(loc.Region, &metapb.Peer{Id: s.peer2, StoreId: s.store2}, 0) 1244 1245 // current leader is peer2 in [peer1, peer2] 1246 loc, err = s.cache.LocateKey(s.bo, []byte("a")) 1247 s.Nil(err) 1248 ctx, err := s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadLeader, 0) 1249 s.Nil(err) 1250 s.Equal(ctx.Peer.StoreId, s.store2) 1251 1252 // simulate peer1 became down in kv heartbeat and loaded before response back. 1253 cpMeta := &metapb.Region{ 1254 Id: ctx.Meta.Id, 1255 StartKey: ctx.Meta.StartKey, 1256 EndKey: ctx.Meta.EndKey, 1257 RegionEpoch: ctx.Meta.RegionEpoch, 1258 Peers: make([]*metapb.Peer, len(ctx.Meta.Peers)), 1259 } 1260 copy(cpMeta.Peers, ctx.Meta.Peers) 1261 cpRegion := &pd.Region{ 1262 Meta: cpMeta, 1263 DownPeers: []*metapb.Peer{{Id: s.peer1, StoreId: s.store1}}, 1264 } 1265 filterUnavailablePeers(cpRegion) 1266 region := &Region{meta: cpRegion.Meta} 1267 err = region.init(s.bo, s.cache) 1268 s.Nil(err) 1269 s.cache.insertRegionToCache(region) 1270 1271 // OnSendFail should not panic 1272 s.cache.OnSendFail(retry.NewNoopBackoff(context.Background()), ctx, false, errors.New("send fail")) 1273 } 1274 1275 func createSampleRegion(startKey, endKey []byte) *Region { 1276 return &Region{ 1277 meta: &metapb.Region{ 1278 StartKey: startKey, 1279 EndKey: endKey, 1280 }, 1281 } 1282 } 1283 1284 func (s *testRegionCacheSuite) TestContains() { 1285 s.True(createSampleRegion(nil, nil).Contains([]byte{})) 1286 s.True(createSampleRegion(nil, nil).Contains([]byte{10})) 1287 s.False(createSampleRegion([]byte{10}, nil).Contains([]byte{})) 1288 s.False(createSampleRegion([]byte{10}, nil).Contains([]byte{9})) 1289 s.True(createSampleRegion([]byte{10}, nil).Contains([]byte{10})) 1290 s.True(createSampleRegion(nil, []byte{10}).Contains([]byte{})) 1291 s.True(createSampleRegion(nil, []byte{10}).Contains([]byte{9})) 1292 s.False(createSampleRegion(nil, []byte{10}).Contains([]byte{10})) 1293 s.False(createSampleRegion([]byte{10}, []byte{20}).Contains([]byte{})) 1294 s.True(createSampleRegion([]byte{10}, []byte{20}).Contains([]byte{15})) 1295 s.False(createSampleRegion([]byte{10}, []byte{20}).Contains([]byte{30})) 1296 } 1297 1298 func (s *testRegionCacheSuite) TestContainsByEnd() { 1299 s.False(createSampleRegion(nil, nil).ContainsByEnd([]byte{})) 1300 s.True(createSampleRegion(nil, nil).ContainsByEnd([]byte{10})) 1301 s.False(createSampleRegion([]byte{10}, nil).ContainsByEnd([]byte{})) 1302 s.False(createSampleRegion([]byte{10}, nil).ContainsByEnd([]byte{10})) 1303 s.True(createSampleRegion([]byte{10}, nil).ContainsByEnd([]byte{11})) 1304 s.False(createSampleRegion(nil, []byte{10}).ContainsByEnd([]byte{})) 1305 s.True(createSampleRegion(nil, []byte{10}).ContainsByEnd([]byte{10})) 1306 s.False(createSampleRegion(nil, []byte{10}).ContainsByEnd([]byte{11})) 1307 s.False(createSampleRegion([]byte{10}, []byte{20}).ContainsByEnd([]byte{})) 1308 s.True(createSampleRegion([]byte{10}, []byte{20}).ContainsByEnd([]byte{15})) 1309 s.False(createSampleRegion([]byte{10}, []byte{20}).ContainsByEnd([]byte{30})) 1310 } 1311 1312 func (s *testRegionCacheSuite) TestSwitchPeerWhenNoLeader() { 1313 var prevCtx *RPCContext 1314 for i := 0; i <= len(s.cluster.GetAllStores()); i++ { 1315 loc, err := s.cache.LocateKey(s.bo, []byte("a")) 1316 s.Nil(err) 1317 ctx, err := s.cache.GetTiKVRPCContext(s.bo, loc.Region, kv.ReplicaReadLeader, 0) 1318 s.Nil(err) 1319 if prevCtx == nil { 1320 s.Equal(i, 0) 1321 } else { 1322 s.NotEqual(ctx.AccessIdx, prevCtx.AccessIdx) 1323 s.NotEqual(ctx.Peer, prevCtx.Peer) 1324 } 1325 s.cache.InvalidateCachedRegionWithReason(loc.Region, NoLeader) 1326 s.Equal(s.cache.GetCachedRegionWithRLock(loc.Region).invalidReason, NoLeader) 1327 prevCtx = ctx 1328 } 1329 } 1330 1331 func BenchmarkOnRequestFail(b *testing.B) { 1332 /* 1333 This benchmark simulate many concurrent requests call OnSendRequestFail method 1334 after failed on a store, validate that on this scene, requests don't get blocked on the 1335 RegionCache lock. 1336 */ 1337 regionCnt, storeCount := 998, 3 1338 cluster := createClusterWithStoresAndRegions(regionCnt, storeCount) 1339 cache := NewRegionCache(mocktikv.NewPDClient(cluster)) 1340 defer cache.Close() 1341 loadRegionsToCache(cache, regionCnt) 1342 bo := retry.NewBackofferWithVars(context.Background(), 1, nil) 1343 loc, err := cache.LocateKey(bo, []byte{}) 1344 if err != nil { 1345 b.Fatal(err) 1346 } 1347 region := cache.getRegionByIDFromCache(loc.Region.id) 1348 b.ResetTimer() 1349 regionStore := region.getStore() 1350 store, peer, accessIdx, _ := region.WorkStorePeer(regionStore) 1351 b.RunParallel(func(pb *testing.PB) { 1352 for pb.Next() { 1353 rpcCtx := &RPCContext{ 1354 Region: loc.Region, 1355 Meta: region.meta, 1356 AccessIdx: accessIdx, 1357 Peer: peer, 1358 Store: store, 1359 AccessMode: tiKVOnly, 1360 } 1361 r := cache.GetCachedRegionWithRLock(rpcCtx.Region) 1362 if r != nil { 1363 r.getStore().switchNextTiKVPeer(r, rpcCtx.AccessIdx) 1364 } 1365 } 1366 }) 1367 if len(cache.mu.regions) != regionCnt*2/3 { 1368 b.Fatal(len(cache.mu.regions)) 1369 } 1370 } 1371 1372 func (s *testRegionCacheSuite) TestNoBackoffWhenFailToDecodeRegion() { 1373 region2 := s.cluster.AllocID() 1374 newPeers := s.cluster.AllocIDs(2) 1375 k := []byte("k") 1376 // Use SplitRaw to split a region with non-memcomparable range keys. 1377 s.cluster.SplitRaw(s.region1, region2, k, newPeers, newPeers[0]) 1378 _, err := s.cache.LocateKey(s.bo, k) 1379 s.NotNil(err) 1380 s.Equal(0, s.bo.GetTotalBackoffTimes()) 1381 _, err = s.cache.LocateRegionByID(s.bo, region2) 1382 s.NotNil(err) 1383 s.Equal(0, s.bo.GetTotalBackoffTimes()) 1384 _, err = s.cache.scanRegions(s.bo, []byte{}, []byte{}, 10) 1385 s.NotNil(err) 1386 s.Equal(0, s.bo.GetTotalBackoffTimes()) 1387 }