github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/allocator_test.go (about) 1 // Copyright 2014 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package kvserver 12 13 import ( 14 "context" 15 "fmt" 16 "math" 17 "os" 18 "reflect" 19 "sort" 20 "strconv" 21 "sync" 22 "testing" 23 "time" 24 25 "github.com/cockroachdb/cockroach/pkg/base" 26 "github.com/cockroachdb/cockroach/pkg/config/zonepb" 27 "github.com/cockroachdb/cockroach/pkg/gossip" 28 "github.com/cockroachdb/cockroach/pkg/keys" 29 "github.com/cockroachdb/cockroach/pkg/kv/kvserver/constraint" 30 "github.com/cockroachdb/cockroach/pkg/kv/kvserver/kvserverpb" 31 "github.com/cockroachdb/cockroach/pkg/roachpb" 32 "github.com/cockroachdb/cockroach/pkg/rpc" 33 "github.com/cockroachdb/cockroach/pkg/settings/cluster" 34 "github.com/cockroachdb/cockroach/pkg/storage/enginepb" 35 "github.com/cockroachdb/cockroach/pkg/testutils" 36 "github.com/cockroachdb/cockroach/pkg/testutils/gossiputil" 37 "github.com/cockroachdb/cockroach/pkg/util" 38 "github.com/cockroachdb/cockroach/pkg/util/hlc" 39 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 40 "github.com/cockroachdb/cockroach/pkg/util/log" 41 "github.com/cockroachdb/cockroach/pkg/util/metric" 42 "github.com/cockroachdb/cockroach/pkg/util/stop" 43 "github.com/cockroachdb/cockroach/pkg/util/timeutil" 44 "github.com/cockroachdb/errors" 45 "github.com/gogo/protobuf/proto" 46 "github.com/olekukonko/tablewriter" 47 "github.com/stretchr/testify/assert" 48 "github.com/stretchr/testify/require" 49 "go.etcd.io/etcd/raft" 50 "go.etcd.io/etcd/raft/tracker" 51 ) 52 53 const firstRangeID = roachpb.RangeID(1) 54 55 var simpleZoneConfig = zonepb.ZoneConfig{ 56 NumReplicas: proto.Int32(1), 57 Constraints: []zonepb.ConstraintsConjunction{ 58 { 59 Constraints: []zonepb.Constraint{ 60 {Value: "a", Type: zonepb.Constraint_REQUIRED}, 61 {Value: "ssd", Type: zonepb.Constraint_REQUIRED}, 62 }, 63 }, 64 }, 65 } 66 67 var multiDCConfig = zonepb.ZoneConfig{ 68 NumReplicas: proto.Int32(2), 69 Constraints: []zonepb.ConstraintsConjunction{ 70 {Constraints: []zonepb.Constraint{{Value: "ssd", Type: zonepb.Constraint_REQUIRED}}}, 71 }, 72 } 73 74 var singleStore = []*roachpb.StoreDescriptor{ 75 { 76 StoreID: 1, 77 Attrs: roachpb.Attributes{Attrs: []string{"ssd"}}, 78 Node: roachpb.NodeDescriptor{ 79 NodeID: 1, 80 Attrs: roachpb.Attributes{Attrs: []string{"a"}}, 81 }, 82 Capacity: roachpb.StoreCapacity{ 83 Capacity: 200, 84 Available: 100, 85 LogicalBytes: 100, 86 }, 87 }, 88 } 89 90 var sameDCStores = []*roachpb.StoreDescriptor{ 91 { 92 StoreID: 1, 93 Attrs: roachpb.Attributes{Attrs: []string{"ssd"}}, 94 Node: roachpb.NodeDescriptor{ 95 NodeID: 1, 96 Attrs: roachpb.Attributes{Attrs: []string{"a"}}, 97 }, 98 Capacity: roachpb.StoreCapacity{ 99 Capacity: 200, 100 Available: 100, 101 LogicalBytes: 100, 102 }, 103 }, 104 { 105 StoreID: 2, 106 Attrs: roachpb.Attributes{Attrs: []string{"ssd"}}, 107 Node: roachpb.NodeDescriptor{ 108 NodeID: 2, 109 Attrs: roachpb.Attributes{Attrs: []string{"a"}}, 110 }, 111 Capacity: roachpb.StoreCapacity{ 112 Capacity: 200, 113 Available: 100, 114 LogicalBytes: 100, 115 }, 116 }, 117 { 118 StoreID: 3, 119 Attrs: roachpb.Attributes{Attrs: []string{"hdd"}}, 120 Node: roachpb.NodeDescriptor{ 121 NodeID: 3, 122 Attrs: roachpb.Attributes{Attrs: []string{"a"}}, 123 }, 124 Capacity: roachpb.StoreCapacity{ 125 Capacity: 200, 126 Available: 100, 127 LogicalBytes: 100, 128 }, 129 }, 130 { 131 StoreID: 4, 132 Attrs: roachpb.Attributes{Attrs: []string{"hdd"}}, 133 Node: roachpb.NodeDescriptor{ 134 NodeID: 4, 135 Attrs: roachpb.Attributes{Attrs: []string{"a"}}, 136 }, 137 Capacity: roachpb.StoreCapacity{ 138 Capacity: 200, 139 Available: 100, 140 LogicalBytes: 100, 141 }, 142 }, 143 { 144 StoreID: 5, 145 Attrs: roachpb.Attributes{Attrs: []string{"mem"}}, 146 Node: roachpb.NodeDescriptor{ 147 NodeID: 5, 148 Attrs: roachpb.Attributes{Attrs: []string{"a"}}, 149 }, 150 Capacity: roachpb.StoreCapacity{ 151 Capacity: 200, 152 Available: 100, 153 LogicalBytes: 100, 154 }, 155 }, 156 } 157 158 var multiDCStores = []*roachpb.StoreDescriptor{ 159 { 160 StoreID: 1, 161 Attrs: roachpb.Attributes{Attrs: []string{"ssd"}}, 162 Node: roachpb.NodeDescriptor{ 163 NodeID: 1, 164 Attrs: roachpb.Attributes{Attrs: []string{"a"}}, 165 }, 166 Capacity: roachpb.StoreCapacity{ 167 Capacity: 200, 168 Available: 100, 169 LogicalBytes: 100, 170 }, 171 }, 172 { 173 StoreID: 2, 174 Attrs: roachpb.Attributes{Attrs: []string{"ssd"}}, 175 Node: roachpb.NodeDescriptor{ 176 NodeID: 2, 177 Attrs: roachpb.Attributes{Attrs: []string{"b"}}, 178 }, 179 Capacity: roachpb.StoreCapacity{ 180 Capacity: 200, 181 Available: 100, 182 LogicalBytes: 100, 183 }, 184 }, 185 } 186 187 var multiDiversityDCStores = []*roachpb.StoreDescriptor{ 188 { 189 StoreID: 1, 190 Attrs: roachpb.Attributes{Attrs: []string{"ssd"}}, 191 Node: roachpb.NodeDescriptor{ 192 NodeID: 1, 193 Attrs: roachpb.Attributes{Attrs: []string{"odd"}}, 194 Locality: roachpb.Locality{ 195 Tiers: []roachpb.Tier{ 196 {Key: "datacenter", Value: "a"}, 197 }, 198 }, 199 }, 200 }, 201 { 202 StoreID: 2, 203 Attrs: roachpb.Attributes{Attrs: []string{"hdd"}}, 204 Node: roachpb.NodeDescriptor{ 205 NodeID: 2, 206 Attrs: roachpb.Attributes{Attrs: []string{"even"}}, 207 Locality: roachpb.Locality{ 208 Tiers: []roachpb.Tier{ 209 {Key: "datacenter", Value: "a"}, 210 }, 211 }, 212 }, 213 }, 214 { 215 StoreID: 3, 216 Attrs: roachpb.Attributes{Attrs: []string{"ssd"}}, 217 Node: roachpb.NodeDescriptor{ 218 NodeID: 3, 219 Attrs: roachpb.Attributes{Attrs: []string{"odd"}}, 220 Locality: roachpb.Locality{ 221 Tiers: []roachpb.Tier{ 222 {Key: "datacenter", Value: "b"}, 223 }, 224 }, 225 }, 226 }, 227 { 228 StoreID: 4, 229 Attrs: roachpb.Attributes{Attrs: []string{"hdd"}}, 230 Node: roachpb.NodeDescriptor{ 231 NodeID: 4, 232 Attrs: roachpb.Attributes{Attrs: []string{"even"}}, 233 Locality: roachpb.Locality{ 234 Tiers: []roachpb.Tier{ 235 {Key: "datacenter", Value: "b"}, 236 }, 237 }, 238 }, 239 }, 240 { 241 StoreID: 5, 242 Attrs: roachpb.Attributes{Attrs: []string{"ssd"}}, 243 Node: roachpb.NodeDescriptor{ 244 NodeID: 5, 245 Attrs: roachpb.Attributes{Attrs: []string{"odd"}}, 246 Locality: roachpb.Locality{ 247 Tiers: []roachpb.Tier{ 248 {Key: "datacenter", Value: "c"}, 249 }, 250 }, 251 }, 252 }, 253 { 254 StoreID: 6, 255 Attrs: roachpb.Attributes{Attrs: []string{"hdd"}}, 256 Node: roachpb.NodeDescriptor{ 257 NodeID: 6, 258 Attrs: roachpb.Attributes{Attrs: []string{"even"}}, 259 Locality: roachpb.Locality{ 260 Tiers: []roachpb.Tier{ 261 {Key: "datacenter", Value: "c"}, 262 }, 263 }, 264 }, 265 }, 266 { 267 StoreID: 7, 268 Attrs: roachpb.Attributes{Attrs: []string{"ssd"}}, 269 Node: roachpb.NodeDescriptor{ 270 NodeID: 7, 271 Attrs: roachpb.Attributes{Attrs: []string{"odd"}}, 272 Locality: roachpb.Locality{ 273 Tiers: []roachpb.Tier{ 274 {Key: "datacenter", Value: "d"}, 275 }, 276 }, 277 }, 278 }, 279 { 280 StoreID: 8, 281 Attrs: roachpb.Attributes{Attrs: []string{"hdd"}}, 282 Node: roachpb.NodeDescriptor{ 283 NodeID: 8, 284 Attrs: roachpb.Attributes{Attrs: []string{"even"}}, 285 Locality: roachpb.Locality{ 286 Tiers: []roachpb.Tier{ 287 {Key: "datacenter", Value: "d"}, 288 }, 289 }, 290 }, 291 }, 292 } 293 294 func replicas(storeIDs ...roachpb.StoreID) []roachpb.ReplicaDescriptor { 295 res := make([]roachpb.ReplicaDescriptor, len(storeIDs)) 296 for i, storeID := range storeIDs { 297 res[i].NodeID = roachpb.NodeID(storeID) 298 res[i].StoreID = storeID 299 res[i].ReplicaID = roachpb.ReplicaID(i + 1) 300 } 301 return res 302 } 303 304 // createTestAllocator creates a stopper, gossip, store pool and allocator for 305 // use in tests. Stopper must be stopped by the caller. 306 func createTestAllocator( 307 numNodes int, deterministic bool, 308 ) (*stop.Stopper, *gossip.Gossip, *StorePool, Allocator, *hlc.ManualClock) { 309 stopper, g, manual, storePool, _ := createTestStorePool( 310 TestTimeUntilStoreDeadOff, deterministic, 311 func() int { return numNodes }, 312 kvserverpb.NodeLivenessStatus_LIVE) 313 a := MakeAllocator(storePool, func(string) (time.Duration, bool) { 314 return 0, true 315 }) 316 return stopper, g, storePool, a, manual 317 } 318 319 // mockStorePool sets up a collection of a alive and dead stores in the store 320 // pool for testing purposes. 321 func mockStorePool( 322 storePool *StorePool, 323 aliveStoreIDs []roachpb.StoreID, 324 unavailableStoreIDs []roachpb.StoreID, 325 deadStoreIDs []roachpb.StoreID, 326 decommissioningStoreIDs []roachpb.StoreID, 327 decommissionedStoreIDs []roachpb.StoreID, 328 ) { 329 storePool.detailsMu.Lock() 330 defer storePool.detailsMu.Unlock() 331 332 liveNodeSet := map[roachpb.NodeID]kvserverpb.NodeLivenessStatus{} 333 storePool.detailsMu.storeDetails = map[roachpb.StoreID]*storeDetail{} 334 for _, storeID := range aliveStoreIDs { 335 liveNodeSet[roachpb.NodeID(storeID)] = kvserverpb.NodeLivenessStatus_LIVE 336 detail := storePool.getStoreDetailLocked(storeID) 337 detail.desc = &roachpb.StoreDescriptor{ 338 StoreID: storeID, 339 Node: roachpb.NodeDescriptor{NodeID: roachpb.NodeID(storeID)}, 340 } 341 } 342 for _, storeID := range unavailableStoreIDs { 343 liveNodeSet[roachpb.NodeID(storeID)] = kvserverpb.NodeLivenessStatus_UNAVAILABLE 344 detail := storePool.getStoreDetailLocked(storeID) 345 detail.desc = &roachpb.StoreDescriptor{ 346 StoreID: storeID, 347 Node: roachpb.NodeDescriptor{NodeID: roachpb.NodeID(storeID)}, 348 } 349 } 350 for _, storeID := range deadStoreIDs { 351 liveNodeSet[roachpb.NodeID(storeID)] = kvserverpb.NodeLivenessStatus_DEAD 352 detail := storePool.getStoreDetailLocked(storeID) 353 detail.desc = &roachpb.StoreDescriptor{ 354 StoreID: storeID, 355 Node: roachpb.NodeDescriptor{NodeID: roachpb.NodeID(storeID)}, 356 } 357 } 358 for _, storeID := range decommissioningStoreIDs { 359 liveNodeSet[roachpb.NodeID(storeID)] = kvserverpb.NodeLivenessStatus_DECOMMISSIONING 360 detail := storePool.getStoreDetailLocked(storeID) 361 detail.desc = &roachpb.StoreDescriptor{ 362 StoreID: storeID, 363 Node: roachpb.NodeDescriptor{NodeID: roachpb.NodeID(storeID)}, 364 } 365 } 366 for _, storeID := range decommissionedStoreIDs { 367 liveNodeSet[roachpb.NodeID(storeID)] = kvserverpb.NodeLivenessStatus_DECOMMISSIONED 368 detail := storePool.getStoreDetailLocked(storeID) 369 detail.desc = &roachpb.StoreDescriptor{ 370 StoreID: storeID, 371 Node: roachpb.NodeDescriptor{NodeID: roachpb.NodeID(storeID)}, 372 } 373 } 374 375 // Set the node liveness function using the set we constructed. 376 storePool.nodeLivenessFn = 377 func(nodeID roachpb.NodeID, now time.Time, threshold time.Duration) kvserverpb.NodeLivenessStatus { 378 if status, ok := liveNodeSet[nodeID]; ok { 379 return status 380 } 381 return kvserverpb.NodeLivenessStatus_UNAVAILABLE 382 } 383 } 384 385 func TestAllocatorSimpleRetrieval(t *testing.T) { 386 defer leaktest.AfterTest(t)() 387 388 stopper, g, _, a, _ := createTestAllocator(1, false /* deterministic */) 389 defer stopper.Stop(context.Background()) 390 gossiputil.NewStoreGossiper(g).GossipStores(singleStore, t) 391 result, _, err := a.AllocateTarget( 392 context.Background(), 393 &simpleZoneConfig, 394 []roachpb.ReplicaDescriptor{}, 395 ) 396 if err != nil { 397 t.Fatalf("Unable to perform allocation: %+v", err) 398 } 399 if result.Node.NodeID != 1 || result.StoreID != 1 { 400 t.Errorf("expected NodeID 1 and StoreID 1: %+v", result) 401 } 402 } 403 404 func TestAllocatorNoAvailableDisks(t *testing.T) { 405 defer leaktest.AfterTest(t)() 406 407 stopper, _, _, a, _ := createTestAllocator(1, false /* deterministic */) 408 defer stopper.Stop(context.Background()) 409 result, _, err := a.AllocateTarget( 410 context.Background(), 411 &simpleZoneConfig, 412 []roachpb.ReplicaDescriptor{}, 413 ) 414 if result != nil { 415 t.Errorf("expected nil result: %+v", result) 416 } 417 if err == nil { 418 t.Errorf("allocation succeeded despite there being no available disks: %v", result) 419 } 420 } 421 422 func TestAllocatorTwoDatacenters(t *testing.T) { 423 defer leaktest.AfterTest(t)() 424 425 stopper, g, _, a, _ := createTestAllocator(1, false /* deterministic */) 426 defer stopper.Stop(context.Background()) 427 gossiputil.NewStoreGossiper(g).GossipStores(multiDCStores, t) 428 ctx := context.Background() 429 result1, _, err := a.AllocateTarget( 430 ctx, 431 &multiDCConfig, 432 []roachpb.ReplicaDescriptor{}, 433 ) 434 if err != nil { 435 t.Fatalf("Unable to perform allocation: %+v", err) 436 } 437 result2, _, err := a.AllocateTarget( 438 ctx, 439 &multiDCConfig, 440 []roachpb.ReplicaDescriptor{{ 441 NodeID: result1.Node.NodeID, 442 StoreID: result1.StoreID, 443 }}, 444 ) 445 if err != nil { 446 t.Fatalf("Unable to perform allocation: %+v", err) 447 } 448 ids := []int{int(result1.Node.NodeID), int(result2.Node.NodeID)} 449 sort.Ints(ids) 450 if expected := []int{1, 2}; !reflect.DeepEqual(ids, expected) { 451 t.Errorf("Expected nodes %+v: %+v vs %+v", expected, result1.Node, result2.Node) 452 } 453 // Verify that no result is forthcoming if we already have a replica. 454 result3, _, err := a.AllocateTarget( 455 ctx, 456 &multiDCConfig, 457 []roachpb.ReplicaDescriptor{ 458 { 459 NodeID: result1.Node.NodeID, 460 StoreID: result1.StoreID, 461 }, 462 { 463 NodeID: result2.Node.NodeID, 464 StoreID: result2.StoreID, 465 }, 466 }, 467 ) 468 if err == nil { 469 t.Errorf("expected error on allocation without available stores: %+v", result3) 470 } 471 } 472 473 func TestAllocatorExistingReplica(t *testing.T) { 474 defer leaktest.AfterTest(t)() 475 476 stopper, g, _, a, _ := createTestAllocator(1, false /* deterministic */) 477 defer stopper.Stop(context.Background()) 478 gossiputil.NewStoreGossiper(g).GossipStores(sameDCStores, t) 479 result, _, err := a.AllocateTarget( 480 context.Background(), 481 &zonepb.ZoneConfig{ 482 NumReplicas: proto.Int32(0), 483 Constraints: []zonepb.ConstraintsConjunction{ 484 { 485 Constraints: []zonepb.Constraint{ 486 {Value: "a", Type: zonepb.Constraint_REQUIRED}, 487 {Value: "hdd", Type: zonepb.Constraint_REQUIRED}, 488 }, 489 }, 490 }, 491 }, 492 []roachpb.ReplicaDescriptor{ 493 { 494 NodeID: 2, 495 StoreID: 2, 496 }, 497 }, 498 ) 499 if err != nil { 500 t.Fatalf("Unable to perform allocation: %+v", err) 501 } 502 if !(result.StoreID == 3 || result.StoreID == 4) { 503 t.Errorf("expected result to have store ID 3 or 4: %+v", result) 504 } 505 } 506 507 func TestAllocatorMultipleStoresPerNode(t *testing.T) { 508 defer leaktest.AfterTest(t)() 509 510 stores := []*roachpb.StoreDescriptor{ 511 { 512 StoreID: 1, 513 Node: roachpb.NodeDescriptor{NodeID: 1}, 514 Capacity: roachpb.StoreCapacity{Capacity: 200, Available: 100, RangeCount: 600}, 515 }, 516 { 517 StoreID: 2, 518 Node: roachpb.NodeDescriptor{NodeID: 1}, 519 Capacity: roachpb.StoreCapacity{Capacity: 200, Available: 100, RangeCount: 500}, 520 }, 521 { 522 StoreID: 3, 523 Node: roachpb.NodeDescriptor{NodeID: 2}, 524 Capacity: roachpb.StoreCapacity{Capacity: 200, Available: 100, RangeCount: 400}, 525 }, 526 { 527 StoreID: 4, 528 Node: roachpb.NodeDescriptor{NodeID: 2}, 529 Capacity: roachpb.StoreCapacity{Capacity: 200, Available: 100, RangeCount: 300}, 530 }, 531 { 532 StoreID: 5, 533 Node: roachpb.NodeDescriptor{NodeID: 3}, 534 Capacity: roachpb.StoreCapacity{Capacity: 200, Available: 100, RangeCount: 200}, 535 }, 536 { 537 StoreID: 6, 538 Node: roachpb.NodeDescriptor{NodeID: 3}, 539 Capacity: roachpb.StoreCapacity{Capacity: 200, Available: 100, RangeCount: 100}, 540 }, 541 } 542 543 stopper, g, _, a, _ := createTestAllocator(10, false /* deterministic */) 544 defer stopper.Stop(context.Background()) 545 gossiputil.NewStoreGossiper(g).GossipStores(stores, t) 546 547 testCases := []struct { 548 existing []roachpb.ReplicaDescriptor 549 expectTarget bool 550 }{ 551 { 552 existing: []roachpb.ReplicaDescriptor{ 553 {NodeID: 1, StoreID: 1}, 554 }, 555 expectTarget: true, 556 }, 557 { 558 existing: []roachpb.ReplicaDescriptor{ 559 {NodeID: 1, StoreID: 2}, 560 {NodeID: 2, StoreID: 3}, 561 }, 562 expectTarget: true, 563 }, 564 { 565 existing: []roachpb.ReplicaDescriptor{ 566 {NodeID: 1, StoreID: 2}, 567 {NodeID: 3, StoreID: 6}, 568 }, 569 expectTarget: true, 570 }, 571 { 572 existing: []roachpb.ReplicaDescriptor{ 573 {NodeID: 1, StoreID: 1}, 574 {NodeID: 2, StoreID: 3}, 575 {NodeID: 3, StoreID: 5}, 576 }, 577 expectTarget: false, 578 }, 579 { 580 existing: []roachpb.ReplicaDescriptor{ 581 {NodeID: 1, StoreID: 2}, 582 {NodeID: 2, StoreID: 4}, 583 {NodeID: 3, StoreID: 6}, 584 }, 585 expectTarget: false, 586 }, 587 } 588 589 for _, tc := range testCases { 590 { 591 result, _, err := a.AllocateTarget( 592 context.Background(), 593 zonepb.EmptyCompleteZoneConfig(), 594 tc.existing, 595 ) 596 if e, a := tc.expectTarget, result != nil; e != a { 597 t.Errorf("AllocateTarget(%v) got target %v, err %v; expectTarget=%v", 598 tc.existing, result, err, tc.expectTarget) 599 } 600 } 601 602 { 603 var rangeUsageInfo RangeUsageInfo 604 target, _, details, ok := a.RebalanceTarget( 605 context.Background(), 606 zonepb.EmptyCompleteZoneConfig(), 607 nil, /* raftStatus */ 608 tc.existing, 609 rangeUsageInfo, 610 storeFilterThrottled, 611 ) 612 if e, a := tc.expectTarget, ok; e != a { 613 t.Errorf("RebalanceTarget(%v) got target %v, details %v; expectTarget=%v", 614 tc.existing, target, details, tc.expectTarget) 615 } 616 } 617 } 618 } 619 620 // TestAllocatorRebalance verifies that rebalance targets are chosen 621 // randomly from amongst stores under the maxFractionUsedThreshold. 622 func TestAllocatorRebalance(t *testing.T) { 623 defer leaktest.AfterTest(t)() 624 625 stores := []*roachpb.StoreDescriptor{ 626 { 627 StoreID: 1, 628 Node: roachpb.NodeDescriptor{NodeID: 1}, 629 Capacity: roachpb.StoreCapacity{Capacity: 100, Available: 100, RangeCount: 1}, 630 }, 631 { 632 StoreID: 2, 633 Node: roachpb.NodeDescriptor{NodeID: 2}, 634 Capacity: roachpb.StoreCapacity{Capacity: 100, Available: 50, RangeCount: 1}, 635 }, 636 { 637 StoreID: 3, 638 Node: roachpb.NodeDescriptor{NodeID: 3}, 639 Capacity: roachpb.StoreCapacity{ 640 Capacity: 100, 641 Available: 100 - int64(100*maxFractionUsedThreshold), 642 RangeCount: 5, 643 }, 644 }, 645 { 646 // This store must not be rebalanced to, because it's too full. 647 StoreID: 4, 648 Node: roachpb.NodeDescriptor{NodeID: 4}, 649 Capacity: roachpb.StoreCapacity{ 650 Capacity: 100, 651 Available: (100 - int64(100*maxFractionUsedThreshold)) / 2, 652 RangeCount: 10, 653 }, 654 }, 655 { 656 // This store will not be rebalanced to, because it already has more 657 // replicas than the mean range count. 658 StoreID: 5, 659 Node: roachpb.NodeDescriptor{NodeID: 5}, 660 Capacity: roachpb.StoreCapacity{ 661 Capacity: 100, 662 Available: 100, 663 RangeCount: 10, 664 }, 665 }, 666 } 667 668 stopper, g, _, a, _ := createTestAllocator(10, false /* deterministic */) 669 defer stopper.Stop(context.Background()) 670 671 gossiputil.NewStoreGossiper(g).GossipStores(stores, t) 672 ctx := context.Background() 673 674 // Every rebalance target must be either store 1 or 2. 675 for i := 0; i < 10; i++ { 676 var rangeUsageInfo RangeUsageInfo 677 target, _, _, ok := a.RebalanceTarget( 678 ctx, 679 zonepb.EmptyCompleteZoneConfig(), 680 nil, 681 []roachpb.ReplicaDescriptor{{NodeID: 3, StoreID: 3}}, 682 rangeUsageInfo, 683 storeFilterThrottled, 684 ) 685 if !ok { 686 i-- // loop until we find 10 candidates 687 continue 688 } 689 // We might not get a rebalance target if the random nodes selected as 690 // candidates are not suitable targets. 691 if target.StoreID != 1 && target.StoreID != 2 { 692 t.Errorf("%d: expected store 1 or 2; got %d", i, target.StoreID) 693 } 694 } 695 696 // Verify shouldRebalance results. 697 for i, store := range stores { 698 desc, ok := a.storePool.getStoreDescriptor(store.StoreID) 699 if !ok { 700 t.Fatalf("%d: unable to get store %d descriptor", i, store.StoreID) 701 } 702 sl, _, _ := a.storePool.getStoreList(storeFilterThrottled) 703 result := shouldRebalance(ctx, desc, sl, a.scorerOptions()) 704 if expResult := (i >= 2); expResult != result { 705 t.Errorf("%d: expected rebalance %t; got %t; desc %+v; sl: %+v", i, expResult, result, desc, sl) 706 } 707 } 708 } 709 710 // TestAllocatorRebalanceTarget could help us to verify whether we'll rebalance 711 // to a target that we'll immediately remove. 712 func TestAllocatorRebalanceTarget(t *testing.T) { 713 defer leaktest.AfterTest(t)() 714 manual := hlc.NewManualClock(123) 715 clock := hlc.NewClock(manual.UnixNano, time.Nanosecond) 716 stopper, g, _, a, _ := createTestAllocator(5, false /* deterministic */) 717 defer stopper.Stop(context.Background()) 718 // We make 5 stores in this test -- 3 in the same datacenter, and 1 each in 719 // 2 other datacenters. All of our replicas are distributed within these 3 720 // datacenters. Originally, the stores that are all alone in their datacenter 721 // are fuller than the other stores. If we didn't simulate RemoveTarget in 722 // RebalanceTarget, we would try to choose store 2 or 3 as the target store 723 // to make a rebalance. However, we would immediately remove the replica on 724 // store 1 or 2 to retain the locality diversity. 725 stores := []*roachpb.StoreDescriptor{ 726 { 727 StoreID: 1, 728 Node: roachpb.NodeDescriptor{ 729 NodeID: 1, 730 Locality: roachpb.Locality{ 731 Tiers: []roachpb.Tier{ 732 {Key: "datacenter", Value: "a"}, 733 }, 734 }, 735 }, 736 Capacity: roachpb.StoreCapacity{ 737 RangeCount: 50, 738 }, 739 }, 740 { 741 StoreID: 2, 742 Node: roachpb.NodeDescriptor{ 743 NodeID: 2, 744 Locality: roachpb.Locality{ 745 Tiers: []roachpb.Tier{ 746 {Key: "datacenter", Value: "a"}, 747 }, 748 }, 749 }, 750 Capacity: roachpb.StoreCapacity{ 751 RangeCount: 55, 752 }, 753 }, 754 { 755 StoreID: 3, 756 Node: roachpb.NodeDescriptor{ 757 NodeID: 3, 758 Locality: roachpb.Locality{ 759 Tiers: []roachpb.Tier{ 760 {Key: "datacenter", Value: "a"}, 761 }, 762 }, 763 }, 764 Capacity: roachpb.StoreCapacity{ 765 RangeCount: 55, 766 }, 767 }, 768 { 769 StoreID: 4, 770 Node: roachpb.NodeDescriptor{ 771 NodeID: 4, 772 Locality: roachpb.Locality{ 773 Tiers: []roachpb.Tier{ 774 {Key: "datacenter", Value: "b"}, 775 }, 776 }, 777 }, 778 Capacity: roachpb.StoreCapacity{ 779 RangeCount: 100, 780 }, 781 }, 782 { 783 StoreID: 5, 784 Node: roachpb.NodeDescriptor{ 785 NodeID: 5, 786 Locality: roachpb.Locality{ 787 Tiers: []roachpb.Tier{ 788 {Key: "datacenter", Value: "c"}, 789 }, 790 }, 791 }, 792 Capacity: roachpb.StoreCapacity{ 793 RangeCount: 100, 794 }, 795 }, 796 } 797 sg := gossiputil.NewStoreGossiper(g) 798 sg.GossipStores(stores, t) 799 800 replicas := []roachpb.ReplicaDescriptor{ 801 {NodeID: 1, StoreID: 1, ReplicaID: 1}, 802 {NodeID: 4, StoreID: 4, ReplicaID: 4}, 803 {NodeID: 5, StoreID: 5, ReplicaID: 5}, 804 } 805 repl := &Replica{RangeID: firstRangeID} 806 807 repl.mu.Lock() 808 repl.mu.state.Stats = &enginepb.MVCCStats{} 809 repl.mu.Unlock() 810 811 repl.leaseholderStats = newReplicaStats(clock, nil) 812 repl.writeStats = newReplicaStats(clock, nil) 813 814 var rangeUsageInfo RangeUsageInfo 815 816 status := &raft.Status{ 817 Progress: make(map[uint64]tracker.Progress), 818 } 819 status.Commit = 10 820 for _, replica := range replicas { 821 status.Progress[uint64(replica.ReplicaID)] = tracker.Progress{ 822 Match: 10, 823 State: tracker.StateReplicate, 824 } 825 } 826 for i := 0; i < 10; i++ { 827 result, _, details, ok := a.RebalanceTarget( 828 context.Background(), 829 zonepb.EmptyCompleteZoneConfig(), 830 status, 831 replicas, 832 rangeUsageInfo, 833 storeFilterThrottled, 834 ) 835 if ok { 836 t.Fatalf("expected no rebalance, but got target s%d; details: %s", result.StoreID, details) 837 } 838 } 839 840 // Set up a second round of testing where the other two stores in the big 841 // locality actually have fewer replicas, but enough that it still isn't 842 // worth rebalancing to them. 843 stores[1].Capacity.RangeCount = 46 844 stores[2].Capacity.RangeCount = 46 845 sg.GossipStores(stores, t) 846 for i := 0; i < 10; i++ { 847 target, _, details, ok := a.RebalanceTarget( 848 context.Background(), 849 zonepb.EmptyCompleteZoneConfig(), 850 status, 851 replicas, 852 rangeUsageInfo, 853 storeFilterThrottled, 854 ) 855 if ok { 856 t.Fatalf("expected no rebalance, but got target s%d; details: %s", target.StoreID, details) 857 } 858 } 859 860 // Make sure rebalancing does happen if we drop just a little further down. 861 stores[1].Capacity.RangeCount = 44 862 sg.GossipStores(stores, t) 863 for i := 0; i < 10; i++ { 864 target, origin, details, ok := a.RebalanceTarget( 865 context.Background(), 866 zonepb.EmptyCompleteZoneConfig(), 867 status, 868 replicas, 869 rangeUsageInfo, 870 storeFilterThrottled, 871 ) 872 expTo := stores[1].StoreID 873 expFrom := stores[0].StoreID 874 if !ok || target.StoreID != expTo || origin.StoreID != expFrom { 875 t.Fatalf("%d: expected rebalance from either of %v to s%d, but got %v->%v; details: %s", 876 i, expFrom, expTo, origin, target, details) 877 } 878 } 879 } 880 881 func TestAllocatorRebalanceDeadNodes(t *testing.T) { 882 defer leaktest.AfterTest(t)() 883 884 stopper, _, sp, a, _ := createTestAllocator(8, false /* deterministic */) 885 ctx := context.Background() 886 defer stopper.Stop(ctx) 887 888 mockStorePool( 889 sp, 890 []roachpb.StoreID{1, 2, 3, 4, 5, 6}, 891 nil, 892 []roachpb.StoreID{7, 8}, 893 nil, 894 nil, 895 ) 896 897 ranges := func(rangeCount int32) roachpb.StoreCapacity { 898 return roachpb.StoreCapacity{ 899 Capacity: 1000, 900 Available: 1000, 901 LogicalBytes: 0, 902 RangeCount: rangeCount, 903 } 904 } 905 906 // Initialize 8 stores: where store 6 is the target for rebalancing. 907 sp.detailsMu.Lock() 908 sp.getStoreDetailLocked(1).desc.Capacity = ranges(100) 909 sp.getStoreDetailLocked(2).desc.Capacity = ranges(100) 910 sp.getStoreDetailLocked(3).desc.Capacity = ranges(100) 911 sp.getStoreDetailLocked(4).desc.Capacity = ranges(100) 912 sp.getStoreDetailLocked(5).desc.Capacity = ranges(100) 913 sp.getStoreDetailLocked(6).desc.Capacity = ranges(0) 914 sp.getStoreDetailLocked(7).desc.Capacity = ranges(100) 915 sp.getStoreDetailLocked(8).desc.Capacity = ranges(100) 916 sp.detailsMu.Unlock() 917 918 // Each test case should describe a repair situation which has a lower 919 // priority than the previous test case. 920 testCases := []struct { 921 existing []roachpb.ReplicaDescriptor 922 expected roachpb.StoreID 923 }{ 924 // 3/3 live -> 3/4 live: ok 925 {replicas(1, 2, 3), 6}, 926 // 2/3 live -> 2/4 live: nope 927 {replicas(1, 2, 7), 0}, 928 // 4/4 live -> 4/5 live: ok 929 {replicas(1, 2, 3, 4), 6}, 930 // 3/4 live -> 3/5 live: ok 931 {replicas(1, 2, 3, 7), 6}, 932 // 5/5 live -> 5/6 live: ok 933 {replicas(1, 2, 3, 4, 5), 6}, 934 // 4/5 live -> 4/6 live: ok 935 {replicas(1, 2, 3, 4, 7), 6}, 936 // 3/5 live -> 3/6 live: nope 937 {replicas(1, 2, 3, 7, 8), 0}, 938 } 939 940 for _, c := range testCases { 941 t.Run("", func(t *testing.T) { 942 var rangeUsageInfo RangeUsageInfo 943 target, _, _, ok := a.RebalanceTarget( 944 ctx, 945 zonepb.EmptyCompleteZoneConfig(), 946 nil, 947 c.existing, 948 rangeUsageInfo, 949 storeFilterThrottled) 950 if c.expected > 0 { 951 if !ok { 952 t.Fatalf("expected %d, but found nil", c.expected) 953 } else if c.expected != target.StoreID { 954 t.Fatalf("expected %d, but found %d", c.expected, target.StoreID) 955 } 956 } else if ok { 957 t.Fatalf("expected nil, but found %d", target.StoreID) 958 } 959 }) 960 } 961 } 962 963 // TestAllocatorRebalanceThrashing tests that the rebalancer does not thrash 964 // when replica counts are balanced, within the appropriate thresholds, across 965 // stores. 966 func TestAllocatorRebalanceThrashing(t *testing.T) { 967 defer leaktest.AfterTest(t)() 968 969 type testStore struct { 970 rangeCount int32 971 shouldRebalanceFrom bool 972 } 973 974 // Returns a slice of stores with the specified mean. The first replica will 975 // have a range count that's above the target range count for the rebalancer, 976 // so it should be rebalanced from. 977 oneStoreAboveRebalanceTarget := func(mean int32, numStores int) func(*cluster.Settings) []testStore { 978 return func(st *cluster.Settings) []testStore { 979 stores := make([]testStore, numStores) 980 for i := range stores { 981 stores[i].rangeCount = mean 982 } 983 surplus := int32(math.Ceil(float64(mean)*rangeRebalanceThreshold.Get(&st.SV) + 1)) 984 stores[0].rangeCount += surplus 985 stores[0].shouldRebalanceFrom = true 986 for i := 1; i < len(stores); i++ { 987 stores[i].rangeCount -= int32(math.Ceil(float64(surplus) / float64(len(stores)-1))) 988 } 989 return stores 990 } 991 } 992 993 // Returns a slice of stores with the specified mean such that the first store 994 // has few enough replicas to make it a rebalance target. 995 oneUnderusedStore := func(mean int32, numStores int) func(*cluster.Settings) []testStore { 996 return func(st *cluster.Settings) []testStore { 997 stores := make([]testStore, numStores) 998 for i := range stores { 999 stores[i].rangeCount = mean 1000 } 1001 // Subtract enough ranges from the first store to make it a suitable 1002 // rebalance target. To maintain the specified mean, we then add that delta 1003 // back to the rest of the replicas. 1004 deficit := int32(math.Ceil(float64(mean)*rangeRebalanceThreshold.Get(&st.SV) + 1)) 1005 stores[0].rangeCount -= deficit 1006 for i := 1; i < len(stores); i++ { 1007 stores[i].rangeCount += int32(math.Ceil(float64(deficit) / float64(len(stores)-1))) 1008 stores[i].shouldRebalanceFrom = true 1009 } 1010 return stores 1011 } 1012 } 1013 1014 // Each test case defines the range counts for the test stores and whether we 1015 // should rebalance from the store. 1016 testCases := []struct { 1017 name string 1018 cluster func(*cluster.Settings) []testStore 1019 }{ 1020 // An evenly balanced cluster should not rebalance. 1021 {"balanced", func(*cluster.Settings) []testStore { 1022 return []testStore{{5, false}, {5, false}, {5, false}, {5, false}} 1023 }}, 1024 // Adding an empty node to a 3-node cluster triggers rebalancing from 1025 // existing nodes. 1026 {"empty-node", func(*cluster.Settings) []testStore { 1027 return []testStore{{100, true}, {100, true}, {100, true}, {0, false}} 1028 }}, 1029 // A cluster where all range counts are within rangeRebalanceThreshold should 1030 // not rebalance. This assumes rangeRebalanceThreshold > 2%. 1031 {"within-threshold", func(*cluster.Settings) []testStore { 1032 return []testStore{{98, false}, {99, false}, {101, false}, {102, false}} 1033 }}, 1034 {"5-stores-mean-100-one-above", oneStoreAboveRebalanceTarget(100, 5)}, 1035 {"5-stores-mean-1000-one-above", oneStoreAboveRebalanceTarget(1000, 5)}, 1036 {"5-stores-mean-10000-one-above", oneStoreAboveRebalanceTarget(10000, 5)}, 1037 1038 {"5-stores-mean-1000-one-underused", oneUnderusedStore(1000, 5)}, 1039 {"10-stores-mean-1000-one-underused", oneUnderusedStore(1000, 10)}, 1040 } 1041 for _, tc := range testCases { 1042 t.Run(tc.name, func(t *testing.T) { 1043 // Deterministic is required when stressing as test case 8 may rebalance 1044 // to different configurations. 1045 stopper, g, _, a, _ := createTestAllocator(1, true /* deterministic */) 1046 defer stopper.Stop(context.Background()) 1047 1048 st := a.storePool.st 1049 cluster := tc.cluster(st) 1050 1051 // It doesn't make sense to test sets of stores containing fewer than 4 1052 // stores, because 4 stores is the minimum number of stores needed to 1053 // trigger rebalancing with the default replication factor of 3. Also, the 1054 // above local functions need a minimum number of stores to properly create 1055 // the desired distribution of range counts. 1056 const minStores = 4 1057 if numStores := len(cluster); numStores < minStores { 1058 t.Fatalf("numStores %d < min %d", numStores, minStores) 1059 } 1060 1061 // Create stores with the range counts from the test case and gossip them. 1062 var stores []*roachpb.StoreDescriptor 1063 for j, store := range cluster { 1064 stores = append(stores, &roachpb.StoreDescriptor{ 1065 StoreID: roachpb.StoreID(j + 1), 1066 Node: roachpb.NodeDescriptor{NodeID: roachpb.NodeID(j + 1)}, 1067 Capacity: roachpb.StoreCapacity{Capacity: 1, Available: 1, RangeCount: store.rangeCount}, 1068 }) 1069 } 1070 gossiputil.NewStoreGossiper(g).GossipStores(stores, t) 1071 1072 // Ensure gossiped store descriptor changes have propagated. 1073 testutils.SucceedsSoon(t, func() error { 1074 sl, _, _ := a.storePool.getStoreList(storeFilterThrottled) 1075 for j, s := range sl.stores { 1076 if a, e := s.Capacity.RangeCount, cluster[j].rangeCount; a != e { 1077 return errors.Errorf("range count for %d = %d != expected %d", j, a, e) 1078 } 1079 } 1080 return nil 1081 }) 1082 sl, _, _ := a.storePool.getStoreList(storeFilterThrottled) 1083 1084 // Verify shouldRebalance returns the expected value. 1085 for j, store := range stores { 1086 desc, ok := a.storePool.getStoreDescriptor(store.StoreID) 1087 if !ok { 1088 t.Fatalf("[store %d]: unable to get store %d descriptor", j, store.StoreID) 1089 } 1090 if a, e := shouldRebalance(context.Background(), desc, sl, a.scorerOptions()), cluster[j].shouldRebalanceFrom; a != e { 1091 t.Errorf("[store %d]: shouldRebalance %t != expected %t", store.StoreID, a, e) 1092 } 1093 } 1094 }) 1095 } 1096 } 1097 1098 // TestAllocatorRebalanceByCount verifies that rebalance targets are 1099 // chosen by range counts in the event that available capacities 1100 // exceed the maxAvailCapacityThreshold. 1101 func TestAllocatorRebalanceByCount(t *testing.T) { 1102 defer leaktest.AfterTest(t)() 1103 1104 // Setup the stores so that only one is below the standard deviation threshold. 1105 stores := []*roachpb.StoreDescriptor{ 1106 { 1107 StoreID: 1, 1108 Node: roachpb.NodeDescriptor{NodeID: 1}, 1109 Capacity: roachpb.StoreCapacity{Capacity: 100, Available: 100, RangeCount: 10}, 1110 }, 1111 { 1112 StoreID: 2, 1113 Node: roachpb.NodeDescriptor{NodeID: 2}, 1114 Capacity: roachpb.StoreCapacity{Capacity: 100, Available: 99, RangeCount: 10}, 1115 }, 1116 { 1117 StoreID: 3, 1118 Node: roachpb.NodeDescriptor{NodeID: 3}, 1119 Capacity: roachpb.StoreCapacity{Capacity: 100, Available: 98, RangeCount: 10}, 1120 }, 1121 { 1122 StoreID: 4, 1123 Node: roachpb.NodeDescriptor{NodeID: 4}, 1124 Capacity: roachpb.StoreCapacity{Capacity: 100, Available: 98, RangeCount: 2}, 1125 }, 1126 } 1127 1128 stopper, g, _, a, _ := createTestAllocator(10, false /* deterministic */) 1129 defer stopper.Stop(context.Background()) 1130 1131 gossiputil.NewStoreGossiper(g).GossipStores(stores, t) 1132 ctx := context.Background() 1133 1134 // Every rebalance target must be store 4 (or nil for case of missing the only option). 1135 for i := 0; i < 10; i++ { 1136 var rangeUsageInfo RangeUsageInfo 1137 result, _, _, ok := a.RebalanceTarget( 1138 ctx, 1139 zonepb.EmptyCompleteZoneConfig(), 1140 nil, 1141 []roachpb.ReplicaDescriptor{{StoreID: stores[0].StoreID}}, 1142 rangeUsageInfo, 1143 storeFilterThrottled, 1144 ) 1145 if ok && result.StoreID != 4 { 1146 t.Errorf("expected store 4; got %d", result.StoreID) 1147 } 1148 } 1149 1150 // Verify shouldRebalance results. 1151 for i, store := range stores { 1152 desc, ok := a.storePool.getStoreDescriptor(store.StoreID) 1153 if !ok { 1154 t.Fatalf("%d: unable to get store %d descriptor", i, store.StoreID) 1155 } 1156 sl, _, _ := a.storePool.getStoreList(storeFilterThrottled) 1157 result := shouldRebalance(ctx, desc, sl, a.scorerOptions()) 1158 if expResult := (i < 3); expResult != result { 1159 t.Errorf("%d: expected rebalance %t; got %t", i, expResult, result) 1160 } 1161 } 1162 } 1163 1164 func TestAllocatorTransferLeaseTarget(t *testing.T) { 1165 defer leaktest.AfterTest(t)() 1166 stopper, g, _, a, _ := createTestAllocator(10, true /* deterministic */) 1167 defer stopper.Stop(context.Background()) 1168 1169 // 3 stores where the lease count for each store is equal to 10x the store 1170 // ID. 1171 var stores []*roachpb.StoreDescriptor 1172 for i := 1; i <= 3; i++ { 1173 stores = append(stores, &roachpb.StoreDescriptor{ 1174 StoreID: roachpb.StoreID(i), 1175 Node: roachpb.NodeDescriptor{NodeID: roachpb.NodeID(i)}, 1176 Capacity: roachpb.StoreCapacity{LeaseCount: int32(10 * i)}, 1177 }) 1178 } 1179 sg := gossiputil.NewStoreGossiper(g) 1180 sg.GossipStores(stores, t) 1181 1182 existing := []roachpb.ReplicaDescriptor{ 1183 {StoreID: 1}, 1184 {StoreID: 2}, 1185 {StoreID: 3}, 1186 } 1187 1188 // TODO(peter): Add test cases for non-empty constraints. 1189 testCases := []struct { 1190 existing []roachpb.ReplicaDescriptor 1191 leaseholder roachpb.StoreID 1192 check bool 1193 expected roachpb.StoreID 1194 }{ 1195 // No existing lease holder, nothing to do. 1196 {existing: existing, leaseholder: 0, check: true, expected: 0}, 1197 // Store 1 is not a lease transfer source. 1198 {existing: existing, leaseholder: 1, check: true, expected: 0}, 1199 {existing: existing, leaseholder: 1, check: false, expected: 2}, 1200 // Store 2 is not a lease transfer source. 1201 {existing: existing, leaseholder: 2, check: true, expected: 0}, 1202 {existing: existing, leaseholder: 2, check: false, expected: 1}, 1203 // Store 3 is a lease transfer source. 1204 {existing: existing, leaseholder: 3, check: true, expected: 1}, 1205 {existing: existing, leaseholder: 3, check: false, expected: 1}, 1206 } 1207 for _, c := range testCases { 1208 t.Run("", func(t *testing.T) { 1209 target := a.TransferLeaseTarget( 1210 context.Background(), 1211 zonepb.EmptyCompleteZoneConfig(), 1212 c.existing, 1213 c.leaseholder, 1214 nil, /* replicaStats */ 1215 c.check, 1216 true, /* checkCandidateFullness */ 1217 false, /* alwaysAllowDecisionWithoutStats */ 1218 ) 1219 if c.expected != target.StoreID { 1220 t.Fatalf("expected %d, but found %d", c.expected, target.StoreID) 1221 } 1222 }) 1223 } 1224 } 1225 1226 // TestAllocatorTransferLeaseTargetDraining verifies that the allocator will 1227 // not choose to transfer leases to a store that is draining. 1228 func TestAllocatorTransferLeaseTargetDraining(t *testing.T) { 1229 defer leaktest.AfterTest(t)() 1230 stopper, g, _, storePool, nl := createTestStorePool( 1231 TestTimeUntilStoreDeadOff, true, /* deterministic */ 1232 func() int { return 10 }, /* nodeCount */ 1233 kvserverpb.NodeLivenessStatus_LIVE) 1234 a := MakeAllocator(storePool, func(string) (time.Duration, bool) { 1235 return 0, true 1236 }) 1237 defer stopper.Stop(context.Background()) 1238 1239 // 3 stores where the lease count for each store is equal to 100x the store 1240 // ID. We'll be draining the store with the fewest leases on it. 1241 var stores []*roachpb.StoreDescriptor 1242 for i := 1; i <= 3; i++ { 1243 stores = append(stores, &roachpb.StoreDescriptor{ 1244 StoreID: roachpb.StoreID(i), 1245 Node: roachpb.NodeDescriptor{NodeID: roachpb.NodeID(i)}, 1246 Capacity: roachpb.StoreCapacity{LeaseCount: int32(100 * i)}, 1247 }) 1248 } 1249 sg := gossiputil.NewStoreGossiper(g) 1250 sg.GossipStores(stores, t) 1251 1252 // UNAVAILABLE is the node liveness status used for a node that's draining. 1253 nl.setNodeStatus(1, kvserverpb.NodeLivenessStatus_UNAVAILABLE) 1254 1255 existing := []roachpb.ReplicaDescriptor{ 1256 {StoreID: 1}, 1257 {StoreID: 2}, 1258 {StoreID: 3}, 1259 } 1260 1261 testCases := []struct { 1262 existing []roachpb.ReplicaDescriptor 1263 leaseholder roachpb.StoreID 1264 check bool 1265 expected roachpb.StoreID 1266 }{ 1267 // No existing lease holder, nothing to do. 1268 {existing: existing, leaseholder: 0, check: true, expected: 0}, 1269 // Store 1 is draining, so it will try to transfer its lease if 1270 // checkTransferLeaseSource is false. This behavior isn't relied upon, 1271 // though; leases are manually transferred when draining. 1272 {existing: existing, leaseholder: 1, check: true, expected: 0}, 1273 {existing: existing, leaseholder: 1, check: false, expected: 2}, 1274 // Store 2 is not a lease transfer source. 1275 {existing: existing, leaseholder: 2, check: true, expected: 0}, 1276 {existing: existing, leaseholder: 2, check: false, expected: 3}, 1277 // Store 3 is a lease transfer source, but won't transfer to 1278 // node 1 because it's draining. 1279 {existing: existing, leaseholder: 3, check: true, expected: 2}, 1280 {existing: existing, leaseholder: 3, check: false, expected: 2}, 1281 } 1282 for _, c := range testCases { 1283 t.Run("", func(t *testing.T) { 1284 target := a.TransferLeaseTarget( 1285 context.Background(), 1286 zonepb.EmptyCompleteZoneConfig(), 1287 c.existing, 1288 c.leaseholder, 1289 nil, /* replicaStats */ 1290 c.check, 1291 true, /* checkCandidateFullness */ 1292 false, /* alwaysAllowDecisionWithoutStats */ 1293 ) 1294 if c.expected != target.StoreID { 1295 t.Fatalf("expected %d, but found %d", c.expected, target.StoreID) 1296 } 1297 }) 1298 } 1299 } 1300 1301 func TestAllocatorRebalanceDifferentLocalitySizes(t *testing.T) { 1302 defer leaktest.AfterTest(t)() 1303 1304 stopper, g, _, a, _ := createTestAllocator(10, false /* deterministic */) 1305 ctx := context.Background() 1306 defer stopper.Stop(ctx) 1307 1308 // Set up 8 stores -- 2 in each of the first 2 localities, and 4 in the third. 1309 // Because of the desire for diversity, the nodes in the small localities end 1310 // up being fuller than the nodes in the large locality. In the past this has 1311 // caused an over-eagerness to rebalance to nodes in the large locality, and 1312 // not enough willingness to rebalance within the small localities. This test 1313 // verifies that we compare fairly amongst stores that will givve the store 1314 // an optimal diversity score, not considering the fullness of those that 1315 // will make for worse diversity. 1316 stores := []*roachpb.StoreDescriptor{ 1317 { 1318 StoreID: 1, 1319 Node: roachpb.NodeDescriptor{ 1320 NodeID: roachpb.NodeID(1), 1321 Locality: roachpb.Locality{ 1322 Tiers: []roachpb.Tier{{Key: "locale", Value: "1"}}, 1323 }, 1324 }, 1325 Capacity: testStoreCapacitySetup(50, 50), 1326 }, 1327 { 1328 StoreID: 2, 1329 Node: roachpb.NodeDescriptor{ 1330 NodeID: roachpb.NodeID(2), 1331 Locality: roachpb.Locality{ 1332 Tiers: []roachpb.Tier{{Key: "locale", Value: "1"}}, 1333 }, 1334 }, 1335 Capacity: testStoreCapacitySetup(40, 60), 1336 }, 1337 { 1338 StoreID: 3, 1339 Node: roachpb.NodeDescriptor{ 1340 NodeID: roachpb.NodeID(3), 1341 Locality: roachpb.Locality{ 1342 Tiers: []roachpb.Tier{{Key: "locale", Value: "2"}}, 1343 }, 1344 }, 1345 Capacity: testStoreCapacitySetup(50, 50), 1346 }, 1347 { 1348 StoreID: 4, 1349 Node: roachpb.NodeDescriptor{ 1350 NodeID: roachpb.NodeID(4), 1351 Locality: roachpb.Locality{ 1352 Tiers: []roachpb.Tier{{Key: "locale", Value: "2"}}, 1353 }, 1354 }, 1355 Capacity: testStoreCapacitySetup(40, 60), 1356 }, 1357 { 1358 StoreID: 5, 1359 Node: roachpb.NodeDescriptor{ 1360 NodeID: roachpb.NodeID(5), 1361 Locality: roachpb.Locality{ 1362 Tiers: []roachpb.Tier{{Key: "locale", Value: "3"}}, 1363 }, 1364 }, 1365 Capacity: testStoreCapacitySetup(90, 10), 1366 }, 1367 { 1368 StoreID: 6, 1369 Node: roachpb.NodeDescriptor{ 1370 NodeID: roachpb.NodeID(6), 1371 Locality: roachpb.Locality{ 1372 Tiers: []roachpb.Tier{{Key: "locale", Value: "3"}}, 1373 }, 1374 }, 1375 Capacity: testStoreCapacitySetup(80, 20), 1376 }, 1377 { 1378 StoreID: 7, 1379 Node: roachpb.NodeDescriptor{ 1380 NodeID: roachpb.NodeID(7), 1381 Locality: roachpb.Locality{ 1382 Tiers: []roachpb.Tier{{Key: "locale", Value: "3"}}, 1383 }, 1384 }, 1385 Capacity: testStoreCapacitySetup(80, 20), 1386 }, 1387 { 1388 StoreID: 8, 1389 Node: roachpb.NodeDescriptor{ 1390 NodeID: roachpb.NodeID(8), 1391 Locality: roachpb.Locality{ 1392 Tiers: []roachpb.Tier{{Key: "locale", Value: "3"}}, 1393 }, 1394 }, 1395 Capacity: testStoreCapacitySetup(80, 20), 1396 }, 1397 } 1398 1399 sg := gossiputil.NewStoreGossiper(g) 1400 sg.GossipStores(stores, t) 1401 1402 testCases := []struct { 1403 existing []roachpb.ReplicaDescriptor 1404 expected roachpb.StoreID // 0 if no rebalance is expected 1405 }{ 1406 {replicas(1, 3, 5), 0}, 1407 {replicas(2, 3, 5), 1}, 1408 {replicas(1, 4, 5), 3}, 1409 {replicas(1, 3, 6), 5}, 1410 {replicas(1, 5, 6), 3}, 1411 {replicas(2, 5, 6), 3}, 1412 {replicas(3, 5, 6), 1}, 1413 {replicas(4, 5, 6), 1}, 1414 } 1415 1416 for i, tc := range testCases { 1417 var rangeUsageInfo RangeUsageInfo 1418 result, _, details, ok := a.RebalanceTarget( 1419 ctx, 1420 zonepb.EmptyCompleteZoneConfig(), 1421 nil, /* raftStatus */ 1422 tc.existing, 1423 rangeUsageInfo, 1424 storeFilterThrottled, 1425 ) 1426 var resultID roachpb.StoreID 1427 if ok { 1428 resultID = result.StoreID 1429 } 1430 if resultID != tc.expected { 1431 t.Errorf("%d: RebalanceTarget(%v) expected s%d; got %v: %s", i, tc.existing, tc.expected, result, details) 1432 } 1433 } 1434 1435 // Add a couple less full nodes in a fourth locality, then run a few more tests: 1436 stores = append(stores, &roachpb.StoreDescriptor{ 1437 StoreID: 9, 1438 Node: roachpb.NodeDescriptor{ 1439 NodeID: roachpb.NodeID(9), 1440 Locality: roachpb.Locality{ 1441 Tiers: []roachpb.Tier{{Key: "locale", Value: "4"}}, 1442 }, 1443 }, 1444 Capacity: testStoreCapacitySetup(70, 30), 1445 }) 1446 stores = append(stores, &roachpb.StoreDescriptor{ 1447 StoreID: 10, 1448 Node: roachpb.NodeDescriptor{ 1449 NodeID: roachpb.NodeID(10), 1450 Locality: roachpb.Locality{ 1451 Tiers: []roachpb.Tier{{Key: "locale", Value: "4"}}, 1452 }, 1453 }, 1454 Capacity: testStoreCapacitySetup(60, 40), 1455 }) 1456 1457 sg.GossipStores(stores, t) 1458 1459 testCases2 := []struct { 1460 existing []roachpb.ReplicaDescriptor 1461 expected []roachpb.StoreID 1462 }{ 1463 {replicas(1, 3, 5), []roachpb.StoreID{9}}, 1464 {replicas(2, 3, 5), []roachpb.StoreID{9}}, 1465 {replicas(1, 4, 5), []roachpb.StoreID{9}}, 1466 {replicas(1, 3, 6), []roachpb.StoreID{9}}, 1467 {replicas(1, 5, 6), []roachpb.StoreID{9}}, 1468 {replicas(2, 5, 6), []roachpb.StoreID{9}}, 1469 {replicas(3, 5, 6), []roachpb.StoreID{9}}, 1470 {replicas(4, 5, 6), []roachpb.StoreID{9}}, 1471 {replicas(5, 6, 7), []roachpb.StoreID{9}}, 1472 {replicas(1, 5, 9), nil}, 1473 {replicas(3, 5, 9), nil}, 1474 {replicas(1, 3, 9), []roachpb.StoreID{5, 6, 7, 8}}, 1475 {replicas(1, 3, 10), []roachpb.StoreID{5, 6, 7, 8}}, 1476 // This last case is a bit more interesting - the difference in range count 1477 // between s10 an s9 is significant enough to motivate a rebalance if they 1478 // were the only two valid options, but they're both considered underful 1479 // relative to the other equally valid placement options (s3 and s4), so 1480 // the system doesn't consider it helpful to rebalance between them. It'd 1481 // prefer to move replicas onto both s9 and s10 from other stores. 1482 {replicas(1, 5, 10), nil}, 1483 } 1484 1485 for i, tc := range testCases2 { 1486 log.Infof(ctx, "case #%d", i) 1487 var rangeUsageInfo RangeUsageInfo 1488 result, _, details, ok := a.RebalanceTarget( 1489 ctx, 1490 zonepb.EmptyCompleteZoneConfig(), 1491 nil, /* raftStatus */ 1492 tc.existing, 1493 rangeUsageInfo, 1494 storeFilterThrottled, 1495 ) 1496 var gotExpected bool 1497 if !ok { 1498 gotExpected = (tc.expected == nil) 1499 } else { 1500 for _, expectedStoreID := range tc.expected { 1501 if result.StoreID == expectedStoreID { 1502 gotExpected = true 1503 break 1504 } 1505 } 1506 } 1507 if !gotExpected { 1508 t.Errorf("%d: RebalanceTarget(%v) expected store in %v; got %v: %s", 1509 i, tc.existing, tc.expected, result, details) 1510 } 1511 } 1512 } 1513 1514 func TestAllocatorTransferLeaseTargetMultiStore(t *testing.T) { 1515 defer leaktest.AfterTest(t)() 1516 stopper, g, _, a, _ := createTestAllocator(10, true /* deterministic */) 1517 defer stopper.Stop(context.Background()) 1518 1519 // 3 nodes and 6 stores where the lease count for the first store on each 1520 // node is equal to 10x the node ID. 1521 var stores []*roachpb.StoreDescriptor 1522 for i := 1; i <= 6; i++ { 1523 node := 1 + (i-1)/2 1524 stores = append(stores, &roachpb.StoreDescriptor{ 1525 StoreID: roachpb.StoreID(i), 1526 Node: roachpb.NodeDescriptor{NodeID: roachpb.NodeID(node)}, 1527 Capacity: roachpb.StoreCapacity{LeaseCount: int32(10 * node * (i % 2))}, 1528 }) 1529 } 1530 sg := gossiputil.NewStoreGossiper(g) 1531 sg.GossipStores(stores, t) 1532 1533 existing := []roachpb.ReplicaDescriptor{ 1534 {NodeID: 1, StoreID: 1}, 1535 {NodeID: 2, StoreID: 3}, 1536 {NodeID: 3, StoreID: 5}, 1537 } 1538 1539 testCases := []struct { 1540 leaseholder roachpb.StoreID 1541 check bool 1542 expected roachpb.StoreID 1543 }{ 1544 {leaseholder: 1, check: false, expected: 3}, 1545 {leaseholder: 1, check: true, expected: 0}, 1546 {leaseholder: 3, check: false, expected: 1}, 1547 {leaseholder: 3, check: true, expected: 0}, 1548 {leaseholder: 5, check: false, expected: 1}, 1549 {leaseholder: 5, check: true, expected: 1}, 1550 } 1551 for _, c := range testCases { 1552 t.Run("", func(t *testing.T) { 1553 target := a.TransferLeaseTarget( 1554 context.Background(), 1555 zonepb.EmptyCompleteZoneConfig(), 1556 existing, 1557 c.leaseholder, 1558 nil, /* replicaStats */ 1559 c.check, 1560 true, /* checkCandidateFullness */ 1561 false, /* alwaysAllowDecisionWithoutStats */ 1562 ) 1563 if c.expected != target.StoreID { 1564 t.Fatalf("expected %d, but found %d", c.expected, target.StoreID) 1565 } 1566 }) 1567 } 1568 } 1569 1570 func TestAllocatorShouldTransferLease(t *testing.T) { 1571 defer leaktest.AfterTest(t)() 1572 stopper, g, _, a, _ := createTestAllocator(10, true /* deterministic */) 1573 defer stopper.Stop(context.Background()) 1574 1575 // 4 stores where the lease count for each store is equal to 10x the store 1576 // ID. 1577 var stores []*roachpb.StoreDescriptor 1578 for i := 1; i <= 4; i++ { 1579 stores = append(stores, &roachpb.StoreDescriptor{ 1580 StoreID: roachpb.StoreID(i), 1581 Node: roachpb.NodeDescriptor{NodeID: roachpb.NodeID(i)}, 1582 Capacity: roachpb.StoreCapacity{LeaseCount: int32(10 * i)}, 1583 }) 1584 } 1585 sg := gossiputil.NewStoreGossiper(g) 1586 sg.GossipStores(stores, t) 1587 1588 testCases := []struct { 1589 leaseholder roachpb.StoreID 1590 existing []roachpb.ReplicaDescriptor 1591 expected bool 1592 }{ 1593 {leaseholder: 1, existing: nil, expected: false}, 1594 {leaseholder: 2, existing: nil, expected: false}, 1595 {leaseholder: 3, existing: nil, expected: false}, 1596 {leaseholder: 4, existing: nil, expected: false}, 1597 {leaseholder: 3, existing: replicas(1), expected: true}, 1598 {leaseholder: 3, existing: replicas(1, 2), expected: true}, 1599 {leaseholder: 3, existing: replicas(2), expected: false}, 1600 {leaseholder: 3, existing: replicas(3), expected: false}, 1601 {leaseholder: 3, existing: replicas(4), expected: false}, 1602 {leaseholder: 4, existing: replicas(1), expected: true}, 1603 {leaseholder: 4, existing: replicas(2), expected: true}, 1604 {leaseholder: 4, existing: replicas(3), expected: true}, 1605 {leaseholder: 4, existing: replicas(1, 2, 3), expected: true}, 1606 } 1607 for _, c := range testCases { 1608 t.Run("", func(t *testing.T) { 1609 result := a.ShouldTransferLease( 1610 context.Background(), 1611 zonepb.EmptyCompleteZoneConfig(), 1612 c.existing, 1613 c.leaseholder, 1614 nil, /* replicaStats */ 1615 ) 1616 if c.expected != result { 1617 t.Fatalf("expected %v, but found %v", c.expected, result) 1618 } 1619 }) 1620 } 1621 } 1622 1623 func TestAllocatorShouldTransferLeaseDraining(t *testing.T) { 1624 defer leaktest.AfterTest(t)() 1625 stopper, g, _, storePool, nl := createTestStorePool( 1626 TestTimeUntilStoreDeadOff, true, /* deterministic */ 1627 func() int { return 10 }, /* nodeCount */ 1628 kvserverpb.NodeLivenessStatus_LIVE) 1629 a := MakeAllocator(storePool, func(string) (time.Duration, bool) { 1630 return 0, true 1631 }) 1632 defer stopper.Stop(context.Background()) 1633 1634 // 4 stores where the lease count for each store is equal to 10x the store 1635 // ID. 1636 var stores []*roachpb.StoreDescriptor 1637 for i := 1; i <= 4; i++ { 1638 stores = append(stores, &roachpb.StoreDescriptor{ 1639 StoreID: roachpb.StoreID(i), 1640 Node: roachpb.NodeDescriptor{NodeID: roachpb.NodeID(i)}, 1641 Capacity: roachpb.StoreCapacity{LeaseCount: int32(10 * i)}, 1642 }) 1643 } 1644 sg := gossiputil.NewStoreGossiper(g) 1645 sg.GossipStores(stores, t) 1646 1647 // UNAVAILABLE is the node liveness status used for a node that's draining. 1648 nl.setNodeStatus(1, kvserverpb.NodeLivenessStatus_UNAVAILABLE) 1649 1650 testCases := []struct { 1651 leaseholder roachpb.StoreID 1652 existing []roachpb.ReplicaDescriptor 1653 expected bool 1654 }{ 1655 {leaseholder: 1, existing: nil, expected: false}, 1656 {leaseholder: 2, existing: nil, expected: false}, 1657 {leaseholder: 3, existing: nil, expected: false}, 1658 {leaseholder: 4, existing: nil, expected: false}, 1659 {leaseholder: 2, existing: replicas(1), expected: false}, 1660 {leaseholder: 3, existing: replicas(1), expected: false}, 1661 {leaseholder: 3, existing: replicas(1, 2), expected: false}, 1662 {leaseholder: 3, existing: replicas(1, 2, 4), expected: false}, 1663 {leaseholder: 4, existing: replicas(1), expected: false}, 1664 {leaseholder: 4, existing: replicas(1, 2), expected: true}, 1665 {leaseholder: 4, existing: replicas(1, 3), expected: true}, 1666 {leaseholder: 4, existing: replicas(1, 2, 3), expected: true}, 1667 } 1668 for _, c := range testCases { 1669 t.Run("", func(t *testing.T) { 1670 result := a.ShouldTransferLease( 1671 context.Background(), 1672 zonepb.EmptyCompleteZoneConfig(), 1673 c.existing, 1674 c.leaseholder, 1675 nil, /* replicaStats */ 1676 ) 1677 if c.expected != result { 1678 t.Fatalf("expected %v, but found %v", c.expected, result) 1679 } 1680 }) 1681 } 1682 } 1683 1684 func TestAllocatorLeasePreferences(t *testing.T) { 1685 defer leaktest.AfterTest(t)() 1686 stopper, g, _, a, _ := createTestAllocator(10, true /* deterministic */) 1687 defer stopper.Stop(context.Background()) 1688 1689 // 4 stores with distinct localities, store attributes, and node attributes 1690 // where the lease count for each store is equal to 100x the store ID. 1691 var stores []*roachpb.StoreDescriptor 1692 for i := 1; i <= 4; i++ { 1693 stores = append(stores, &roachpb.StoreDescriptor{ 1694 StoreID: roachpb.StoreID(i), 1695 Attrs: roachpb.Attributes{Attrs: []string{fmt.Sprintf("s%d", i)}}, 1696 Node: roachpb.NodeDescriptor{ 1697 NodeID: roachpb.NodeID(i), 1698 Attrs: roachpb.Attributes{Attrs: []string{fmt.Sprintf("n%d", i)}}, 1699 Locality: roachpb.Locality{ 1700 Tiers: []roachpb.Tier{ 1701 {Key: "dc", Value: strconv.Itoa(i)}, 1702 }, 1703 }, 1704 }, 1705 Capacity: roachpb.StoreCapacity{LeaseCount: int32(100 * i)}, 1706 }) 1707 } 1708 sg := gossiputil.NewStoreGossiper(g) 1709 sg.GossipStores(stores, t) 1710 1711 preferDC1 := []zonepb.LeasePreference{ 1712 {Constraints: []zonepb.Constraint{{Key: "dc", Value: "1", Type: zonepb.Constraint_REQUIRED}}}, 1713 } 1714 preferDC4Then3Then2 := []zonepb.LeasePreference{ 1715 {Constraints: []zonepb.Constraint{{Key: "dc", Value: "4", Type: zonepb.Constraint_REQUIRED}}}, 1716 {Constraints: []zonepb.Constraint{{Key: "dc", Value: "3", Type: zonepb.Constraint_REQUIRED}}}, 1717 {Constraints: []zonepb.Constraint{{Key: "dc", Value: "2", Type: zonepb.Constraint_REQUIRED}}}, 1718 } 1719 preferN2ThenS3 := []zonepb.LeasePreference{ 1720 {Constraints: []zonepb.Constraint{{Value: "n2", Type: zonepb.Constraint_REQUIRED}}}, 1721 {Constraints: []zonepb.Constraint{{Value: "s3", Type: zonepb.Constraint_REQUIRED}}}, 1722 } 1723 preferNotS1ThenNotN2 := []zonepb.LeasePreference{ 1724 {Constraints: []zonepb.Constraint{{Value: "s1", Type: zonepb.Constraint_PROHIBITED}}}, 1725 {Constraints: []zonepb.Constraint{{Value: "n2", Type: zonepb.Constraint_PROHIBITED}}}, 1726 } 1727 preferNotS1AndNotN2 := []zonepb.LeasePreference{ 1728 { 1729 Constraints: []zonepb.Constraint{ 1730 {Value: "s1", Type: zonepb.Constraint_PROHIBITED}, 1731 {Value: "n2", Type: zonepb.Constraint_PROHIBITED}, 1732 }, 1733 }, 1734 } 1735 preferMatchesNothing := []zonepb.LeasePreference{ 1736 {Constraints: []zonepb.Constraint{{Key: "dc", Value: "5", Type: zonepb.Constraint_REQUIRED}}}, 1737 {Constraints: []zonepb.Constraint{{Value: "n6", Type: zonepb.Constraint_REQUIRED}}}, 1738 } 1739 1740 testCases := []struct { 1741 leaseholder roachpb.StoreID 1742 existing []roachpb.ReplicaDescriptor 1743 preferences []zonepb.LeasePreference 1744 expectedCheckTrue roachpb.StoreID /* checkTransferLeaseSource = true */ 1745 expectedCheckFalse roachpb.StoreID /* checkTransferLeaseSource = false */ 1746 }{ 1747 {1, nil, preferDC1, 0, 0}, 1748 {1, replicas(1, 2, 3, 4), preferDC1, 0, 2}, 1749 {1, replicas(2, 3, 4), preferDC1, 0, 2}, 1750 {2, replicas(1, 2, 3, 4), preferDC1, 1, 1}, 1751 {2, replicas(2, 3, 4), preferDC1, 0, 3}, 1752 {4, replicas(2, 3, 4), preferDC1, 2, 2}, 1753 {1, nil, preferDC4Then3Then2, 0, 0}, 1754 {1, replicas(1, 2, 3, 4), preferDC4Then3Then2, 4, 4}, 1755 {1, replicas(1, 2, 3), preferDC4Then3Then2, 3, 3}, 1756 {1, replicas(1, 2), preferDC4Then3Then2, 2, 2}, 1757 {3, replicas(1, 2, 3, 4), preferDC4Then3Then2, 4, 4}, 1758 {3, replicas(1, 2, 3), preferDC4Then3Then2, 0, 2}, 1759 {3, replicas(1, 3), preferDC4Then3Then2, 0, 1}, 1760 {4, replicas(1, 2, 3, 4), preferDC4Then3Then2, 0, 3}, 1761 {4, replicas(1, 2, 4), preferDC4Then3Then2, 0, 2}, 1762 {4, replicas(1, 4), preferDC4Then3Then2, 0, 1}, 1763 {1, replicas(1, 2, 3, 4), preferN2ThenS3, 2, 2}, 1764 {1, replicas(1, 3, 4), preferN2ThenS3, 3, 3}, 1765 {1, replicas(1, 4), preferN2ThenS3, 0, 4}, 1766 {2, replicas(1, 2, 3, 4), preferN2ThenS3, 0, 3}, 1767 {2, replicas(1, 2, 4), preferN2ThenS3, 0, 1}, 1768 {3, replicas(1, 2, 3, 4), preferN2ThenS3, 2, 2}, 1769 {3, replicas(1, 3, 4), preferN2ThenS3, 0, 1}, 1770 {4, replicas(1, 4), preferN2ThenS3, 1, 1}, 1771 {1, replicas(1, 2, 3, 4), preferNotS1ThenNotN2, 2, 2}, 1772 {1, replicas(1, 3, 4), preferNotS1ThenNotN2, 3, 3}, 1773 {1, replicas(1, 2), preferNotS1ThenNotN2, 2, 2}, 1774 {1, replicas(1), preferNotS1ThenNotN2, 0, 0}, 1775 {2, replicas(1, 2, 3, 4), preferNotS1ThenNotN2, 0, 3}, 1776 {2, replicas(2, 3, 4), preferNotS1ThenNotN2, 0, 3}, 1777 {2, replicas(1, 2, 3), preferNotS1ThenNotN2, 0, 3}, 1778 {2, replicas(1, 2, 4), preferNotS1ThenNotN2, 0, 4}, 1779 {4, replicas(1, 2, 3, 4), preferNotS1ThenNotN2, 2, 2}, 1780 {4, replicas(1, 4), preferNotS1ThenNotN2, 0, 1}, 1781 {1, replicas(1, 2, 3, 4), preferNotS1AndNotN2, 3, 3}, 1782 {1, replicas(1, 2), preferNotS1AndNotN2, 0, 2}, 1783 {2, replicas(1, 2, 3, 4), preferNotS1AndNotN2, 3, 3}, 1784 {2, replicas(2, 3, 4), preferNotS1AndNotN2, 3, 3}, 1785 {2, replicas(1, 2, 3), preferNotS1AndNotN2, 3, 3}, 1786 {2, replicas(1, 2, 4), preferNotS1AndNotN2, 4, 4}, 1787 {3, replicas(1, 3), preferNotS1AndNotN2, 0, 1}, 1788 {4, replicas(1, 4), preferNotS1AndNotN2, 0, 1}, 1789 {1, replicas(1, 2, 3, 4), preferMatchesNothing, 0, 2}, 1790 {2, replicas(1, 2, 3, 4), preferMatchesNothing, 0, 1}, 1791 {3, replicas(1, 3, 4), preferMatchesNothing, 1, 1}, 1792 {4, replicas(1, 3, 4), preferMatchesNothing, 1, 1}, 1793 {4, replicas(2, 3, 4), preferMatchesNothing, 2, 2}, 1794 } 1795 1796 for _, c := range testCases { 1797 t.Run("", func(t *testing.T) { 1798 zone := &zonepb.ZoneConfig{NumReplicas: proto.Int32(0), LeasePreferences: c.preferences} 1799 result := a.ShouldTransferLease( 1800 context.Background(), 1801 zone, 1802 c.existing, 1803 c.leaseholder, 1804 nil, /* replicaStats */ 1805 ) 1806 expectTransfer := c.expectedCheckTrue != 0 1807 if expectTransfer != result { 1808 t.Errorf("expected %v, but found %v", expectTransfer, result) 1809 } 1810 target := a.TransferLeaseTarget( 1811 context.Background(), 1812 zone, 1813 c.existing, 1814 c.leaseholder, 1815 nil, /* replicaStats */ 1816 true, /* checkTransferLeaseSource */ 1817 true, /* checkCandidateFullness */ 1818 false, /* alwaysAllowDecisionWithoutStats */ 1819 ) 1820 if c.expectedCheckTrue != target.StoreID { 1821 t.Errorf("expected s%d for check=true, but found %v", c.expectedCheckTrue, target) 1822 } 1823 target = a.TransferLeaseTarget( 1824 context.Background(), 1825 zone, 1826 c.existing, 1827 c.leaseholder, 1828 nil, /* replicaStats */ 1829 false, /* checkTransferLeaseSource */ 1830 true, /* checkCandidateFullness */ 1831 false, /* alwaysAllowDecisionWithoutStats */ 1832 ) 1833 if c.expectedCheckFalse != target.StoreID { 1834 t.Errorf("expected s%d for check=false, but found %v", c.expectedCheckFalse, target) 1835 } 1836 }) 1837 } 1838 } 1839 1840 func TestAllocatorLeasePreferencesMultipleStoresPerLocality(t *testing.T) { 1841 defer leaktest.AfterTest(t)() 1842 stopper, g, _, a, _ := createTestAllocator(10, true /* deterministic */) 1843 defer stopper.Stop(context.Background()) 1844 1845 // 6 stores, 2 in each of 3 distinct localities. 1846 var stores []*roachpb.StoreDescriptor 1847 for i := 1; i <= 6; i++ { 1848 var region, zone string 1849 if i <= 2 { 1850 region = "us-east1" 1851 zone = "us-east1-a" 1852 } else if i <= 4 { 1853 region = "us-east1" 1854 zone = "us-east1-b" 1855 } else { 1856 region = "us-west1" 1857 zone = "us-west1-a" 1858 } 1859 stores = append(stores, &roachpb.StoreDescriptor{ 1860 StoreID: roachpb.StoreID(i), 1861 Node: roachpb.NodeDescriptor{ 1862 NodeID: roachpb.NodeID(i), 1863 Locality: roachpb.Locality{ 1864 Tiers: []roachpb.Tier{ 1865 {Key: "region", Value: region}, 1866 {Key: "zone", Value: zone}, 1867 }, 1868 }, 1869 }, 1870 Capacity: roachpb.StoreCapacity{LeaseCount: int32(100 * i)}, 1871 }) 1872 } 1873 sg := gossiputil.NewStoreGossiper(g) 1874 sg.GossipStores(stores, t) 1875 1876 preferEast := []zonepb.LeasePreference{ 1877 {Constraints: []zonepb.Constraint{{Key: "region", Value: "us-east1", Type: zonepb.Constraint_REQUIRED}}}, 1878 } 1879 preferNotEast := []zonepb.LeasePreference{ 1880 {Constraints: []zonepb.Constraint{{Key: "region", Value: "us-east1", Type: zonepb.Constraint_PROHIBITED}}}, 1881 } 1882 1883 testCases := []struct { 1884 leaseholder roachpb.StoreID 1885 existing []roachpb.ReplicaDescriptor 1886 preferences []zonepb.LeasePreference 1887 expectedCheckTrue roachpb.StoreID /* checkTransferLeaseSource = true */ 1888 expectedCheckFalse roachpb.StoreID /* checkTransferLeaseSource = false */ 1889 }{ 1890 {1, replicas(1, 3, 5), preferEast, 0, 3}, 1891 {1, replicas(1, 2, 3), preferEast, 0, 2}, 1892 {3, replicas(1, 3, 5), preferEast, 0, 1}, 1893 {5, replicas(1, 4, 5), preferEast, 1, 1}, 1894 {5, replicas(3, 4, 5), preferEast, 3, 3}, 1895 {1, replicas(1, 5, 6), preferEast, 0, 5}, 1896 {1, replicas(1, 3, 5), preferNotEast, 5, 5}, 1897 {1, replicas(1, 5, 6), preferNotEast, 5, 5}, 1898 {3, replicas(1, 3, 5), preferNotEast, 5, 5}, 1899 {5, replicas(1, 5, 6), preferNotEast, 0, 6}, 1900 } 1901 1902 for _, c := range testCases { 1903 t.Run("", func(t *testing.T) { 1904 zone := &zonepb.ZoneConfig{NumReplicas: proto.Int32(0), LeasePreferences: c.preferences} 1905 target := a.TransferLeaseTarget( 1906 context.Background(), 1907 zone, 1908 c.existing, 1909 c.leaseholder, 1910 nil, /* replicaStats */ 1911 true, /* checkTransferLeaseSource */ 1912 true, /* checkCandidateFullness */ 1913 false, /* alwaysAllowDecisionWithoutStats */ 1914 ) 1915 if c.expectedCheckTrue != target.StoreID { 1916 t.Errorf("expected s%d for check=true, but found %v", c.expectedCheckTrue, target) 1917 } 1918 target = a.TransferLeaseTarget( 1919 context.Background(), 1920 zone, 1921 c.existing, 1922 c.leaseholder, 1923 nil, /* replicaStats */ 1924 false, /* checkTransferLeaseSource */ 1925 true, /* checkCandidateFullness */ 1926 false, /* alwaysAllowDecisionWithoutStats */ 1927 ) 1928 if c.expectedCheckFalse != target.StoreID { 1929 t.Errorf("expected s%d for check=false, but found %v", c.expectedCheckFalse, target) 1930 } 1931 }) 1932 } 1933 } 1934 1935 func TestAllocatorRemoveTargetLocality(t *testing.T) { 1936 defer leaktest.AfterTest(t)() 1937 1938 stopper, g, _, a, _ := createTestAllocator(10, false /* deterministic */) 1939 defer stopper.Stop(context.Background()) 1940 sg := gossiputil.NewStoreGossiper(g) 1941 sg.GossipStores(multiDiversityDCStores, t) 1942 1943 // Given a set of existing replicas for a range, pick out the ones that should 1944 // be removed purely on the basis of locality diversity. 1945 testCases := []struct { 1946 existing []roachpb.StoreID 1947 expected []roachpb.StoreID 1948 }{ 1949 { 1950 []roachpb.StoreID{1, 2, 3, 5}, 1951 []roachpb.StoreID{1, 2}, 1952 }, 1953 { 1954 []roachpb.StoreID{1, 2, 3}, 1955 []roachpb.StoreID{1, 2}, 1956 }, 1957 { 1958 []roachpb.StoreID{1, 3, 4, 5}, 1959 []roachpb.StoreID{3, 4}, 1960 }, 1961 { 1962 []roachpb.StoreID{1, 3, 5, 6}, 1963 []roachpb.StoreID{5, 6}, 1964 }, 1965 { 1966 []roachpb.StoreID{1, 3, 5}, 1967 []roachpb.StoreID{1, 3, 5}, 1968 }, 1969 { 1970 []roachpb.StoreID{1, 3, 4, 6, 7, 8}, 1971 []roachpb.StoreID{3, 4, 7, 8}, 1972 }, 1973 } 1974 for _, c := range testCases { 1975 existingRepls := make([]roachpb.ReplicaDescriptor, len(c.existing)) 1976 for i, storeID := range c.existing { 1977 existingRepls[i] = roachpb.ReplicaDescriptor{ 1978 NodeID: roachpb.NodeID(storeID), 1979 StoreID: storeID, 1980 } 1981 } 1982 targetRepl, details, err := a.RemoveTarget( 1983 context.Background(), 1984 zonepb.EmptyCompleteZoneConfig(), 1985 existingRepls, 1986 existingRepls, 1987 ) 1988 if err != nil { 1989 t.Fatal(err) 1990 } 1991 var found bool 1992 for _, storeID := range c.expected { 1993 if targetRepl.StoreID == storeID { 1994 found = true 1995 break 1996 } 1997 } 1998 if !found { 1999 t.Errorf("expected RemoveTarget(%v) in %v, but got %d; details: %s", c.existing, c.expected, targetRepl.StoreID, details) 2000 } 2001 } 2002 } 2003 2004 func TestAllocatorAllocateTargetLocality(t *testing.T) { 2005 defer leaktest.AfterTest(t)() 2006 2007 stopper, g, _, a, _ := createTestAllocator(10, false /* deterministic */) 2008 defer stopper.Stop(context.Background()) 2009 sg := gossiputil.NewStoreGossiper(g) 2010 sg.GossipStores(multiDiversityDCStores, t) 2011 2012 // Given a set of existing replicas for a range, rank which of the remaining 2013 // stores from multiDiversityDCStores would be the best addition to the range 2014 // purely on the basis of locality diversity. 2015 testCases := []struct { 2016 existing []roachpb.StoreID 2017 expected []roachpb.StoreID 2018 }{ 2019 { 2020 []roachpb.StoreID{1, 2, 3}, 2021 []roachpb.StoreID{5, 6, 7, 8}, 2022 }, 2023 { 2024 []roachpb.StoreID{1, 3, 4}, 2025 []roachpb.StoreID{5, 6, 7, 8}, 2026 }, 2027 { 2028 []roachpb.StoreID{3, 4, 5}, 2029 []roachpb.StoreID{1, 2, 7, 8}, 2030 }, 2031 { 2032 []roachpb.StoreID{1, 7, 8}, 2033 []roachpb.StoreID{3, 4, 5, 6}, 2034 }, 2035 { 2036 []roachpb.StoreID{5, 7, 8}, 2037 []roachpb.StoreID{1, 2, 3, 4}, 2038 }, 2039 { 2040 []roachpb.StoreID{1, 3, 5}, 2041 []roachpb.StoreID{7, 8}, 2042 }, 2043 { 2044 []roachpb.StoreID{1, 3, 7}, 2045 []roachpb.StoreID{5, 6}, 2046 }, 2047 { 2048 []roachpb.StoreID{1, 5, 7}, 2049 []roachpb.StoreID{3, 4}, 2050 }, 2051 { 2052 []roachpb.StoreID{3, 5, 7}, 2053 []roachpb.StoreID{1, 2}, 2054 }, 2055 } 2056 2057 for _, c := range testCases { 2058 existingRepls := make([]roachpb.ReplicaDescriptor, len(c.existing)) 2059 for i, storeID := range c.existing { 2060 existingRepls[i] = roachpb.ReplicaDescriptor{ 2061 NodeID: roachpb.NodeID(storeID), 2062 StoreID: storeID, 2063 } 2064 } 2065 targetStore, details, err := a.AllocateTarget( 2066 context.Background(), 2067 zonepb.EmptyCompleteZoneConfig(), 2068 existingRepls, 2069 ) 2070 if err != nil { 2071 t.Fatal(err) 2072 } 2073 var found bool 2074 for _, storeID := range c.expected { 2075 if targetStore.StoreID == storeID { 2076 found = true 2077 break 2078 } 2079 } 2080 if !found { 2081 t.Errorf("expected AllocateTarget(%v) in %v, but got %d; details: %s", c.existing, c.expected, targetStore.StoreID, details) 2082 } 2083 } 2084 } 2085 2086 func TestAllocatorRebalanceTargetLocality(t *testing.T) { 2087 defer leaktest.AfterTest(t)() 2088 2089 stopper, g, _, a, _ := createTestAllocator(10, false /* deterministic */) 2090 defer stopper.Stop(context.Background()) 2091 2092 stores := []*roachpb.StoreDescriptor{ 2093 { 2094 StoreID: 1, 2095 Node: multiDiversityDCStores[0].Node, 2096 Capacity: roachpb.StoreCapacity{RangeCount: 10}, 2097 }, 2098 { 2099 StoreID: 2, 2100 Node: multiDiversityDCStores[1].Node, 2101 Capacity: roachpb.StoreCapacity{RangeCount: 20}, 2102 }, 2103 { 2104 StoreID: 3, 2105 Node: multiDiversityDCStores[2].Node, 2106 Capacity: roachpb.StoreCapacity{RangeCount: 10}, 2107 }, 2108 { 2109 StoreID: 4, 2110 Node: multiDiversityDCStores[3].Node, 2111 Capacity: roachpb.StoreCapacity{RangeCount: 20}, 2112 }, 2113 { 2114 StoreID: 5, 2115 Node: multiDiversityDCStores[4].Node, 2116 Capacity: roachpb.StoreCapacity{RangeCount: 10}, 2117 }, 2118 { 2119 StoreID: 6, 2120 Node: multiDiversityDCStores[5].Node, 2121 Capacity: roachpb.StoreCapacity{RangeCount: 20}, 2122 }, 2123 } 2124 sg := gossiputil.NewStoreGossiper(g) 2125 sg.GossipStores(stores, t) 2126 2127 testCases := []struct { 2128 existing []roachpb.StoreID 2129 expected []roachpb.StoreID 2130 }{ 2131 { 2132 []roachpb.StoreID{1, 2, 3}, 2133 []roachpb.StoreID{5}, 2134 }, 2135 { 2136 []roachpb.StoreID{1, 3, 4}, 2137 []roachpb.StoreID{5}, 2138 }, 2139 { 2140 []roachpb.StoreID{1, 3, 6}, 2141 []roachpb.StoreID{5}, 2142 }, 2143 { 2144 []roachpb.StoreID{1, 2, 5}, 2145 []roachpb.StoreID{3}, 2146 }, 2147 { 2148 []roachpb.StoreID{1, 2, 6}, 2149 []roachpb.StoreID{3}, 2150 }, 2151 { 2152 []roachpb.StoreID{1, 4, 5}, 2153 []roachpb.StoreID{3}, 2154 }, 2155 { 2156 []roachpb.StoreID{1, 4, 6}, 2157 []roachpb.StoreID{3, 5}, 2158 }, 2159 { 2160 []roachpb.StoreID{3, 4, 5}, 2161 []roachpb.StoreID{1}, 2162 }, 2163 { 2164 []roachpb.StoreID{3, 4, 6}, 2165 []roachpb.StoreID{1}, 2166 }, 2167 { 2168 []roachpb.StoreID{4, 5, 6}, 2169 []roachpb.StoreID{1}, 2170 }, 2171 { 2172 []roachpb.StoreID{2, 4, 6}, 2173 []roachpb.StoreID{1, 3, 5}, 2174 }, 2175 } 2176 2177 for i, c := range testCases { 2178 existingRepls := make([]roachpb.ReplicaDescriptor, len(c.existing)) 2179 for i, storeID := range c.existing { 2180 existingRepls[i] = roachpb.ReplicaDescriptor{ 2181 NodeID: roachpb.NodeID(storeID), 2182 StoreID: storeID, 2183 } 2184 } 2185 var rangeUsageInfo RangeUsageInfo 2186 target, _, details, ok := a.RebalanceTarget( 2187 context.Background(), 2188 zonepb.EmptyCompleteZoneConfig(), 2189 nil, 2190 existingRepls, 2191 rangeUsageInfo, 2192 storeFilterThrottled, 2193 ) 2194 if !ok { 2195 t.Fatalf("%d: RebalanceTarget(%v) returned no target store; details: %s", i, c.existing, details) 2196 } 2197 var found bool 2198 for _, storeID := range c.expected { 2199 if target.StoreID == storeID { 2200 found = true 2201 break 2202 } 2203 } 2204 if !found { 2205 t.Errorf("%d: expected RebalanceTarget(%v) in %v, but got %d; details: %s", 2206 i, c.existing, c.expected, target.StoreID, details) 2207 } 2208 } 2209 } 2210 2211 var ( 2212 threeSpecificLocalities = []zonepb.ConstraintsConjunction{ 2213 { 2214 Constraints: []zonepb.Constraint{ 2215 {Key: "datacenter", Value: "a", Type: zonepb.Constraint_REQUIRED}, 2216 }, 2217 NumReplicas: 1, 2218 }, 2219 { 2220 Constraints: []zonepb.Constraint{ 2221 {Key: "datacenter", Value: "b", Type: zonepb.Constraint_REQUIRED}, 2222 }, 2223 NumReplicas: 1, 2224 }, 2225 { 2226 Constraints: []zonepb.Constraint{ 2227 {Key: "datacenter", Value: "c", Type: zonepb.Constraint_REQUIRED}, 2228 }, 2229 NumReplicas: 1, 2230 }, 2231 } 2232 2233 twoAndOneLocalities = []zonepb.ConstraintsConjunction{ 2234 { 2235 Constraints: []zonepb.Constraint{ 2236 {Key: "datacenter", Value: "a", Type: zonepb.Constraint_REQUIRED}, 2237 }, 2238 NumReplicas: 2, 2239 }, 2240 { 2241 Constraints: []zonepb.Constraint{ 2242 {Key: "datacenter", Value: "b", Type: zonepb.Constraint_REQUIRED}, 2243 }, 2244 NumReplicas: 1, 2245 }, 2246 } 2247 2248 threeInOneLocality = []zonepb.ConstraintsConjunction{ 2249 { 2250 Constraints: []zonepb.Constraint{ 2251 {Key: "datacenter", Value: "a", Type: zonepb.Constraint_REQUIRED}, 2252 }, 2253 NumReplicas: 3, 2254 }, 2255 } 2256 2257 twoAndOneNodeAttrs = []zonepb.ConstraintsConjunction{ 2258 { 2259 Constraints: []zonepb.Constraint{ 2260 {Value: "ssd", Type: zonepb.Constraint_REQUIRED}, 2261 }, 2262 NumReplicas: 2, 2263 }, 2264 { 2265 Constraints: []zonepb.Constraint{ 2266 {Value: "hdd", Type: zonepb.Constraint_REQUIRED}, 2267 }, 2268 NumReplicas: 1, 2269 }, 2270 } 2271 2272 twoAndOneStoreAttrs = []zonepb.ConstraintsConjunction{ 2273 { 2274 Constraints: []zonepb.Constraint{ 2275 {Value: "odd", Type: zonepb.Constraint_REQUIRED}, 2276 }, 2277 NumReplicas: 2, 2278 }, 2279 { 2280 Constraints: []zonepb.Constraint{ 2281 {Value: "even", Type: zonepb.Constraint_REQUIRED}, 2282 }, 2283 NumReplicas: 1, 2284 }, 2285 } 2286 2287 mixLocalityAndAttrs = []zonepb.ConstraintsConjunction{ 2288 { 2289 Constraints: []zonepb.Constraint{ 2290 {Key: "datacenter", Value: "a", Type: zonepb.Constraint_REQUIRED}, 2291 {Value: "ssd", Type: zonepb.Constraint_REQUIRED}, 2292 }, 2293 NumReplicas: 1, 2294 }, 2295 { 2296 Constraints: []zonepb.Constraint{ 2297 {Key: "datacenter", Value: "b", Type: zonepb.Constraint_REQUIRED}, 2298 {Value: "odd", Type: zonepb.Constraint_REQUIRED}, 2299 }, 2300 NumReplicas: 1, 2301 }, 2302 { 2303 Constraints: []zonepb.Constraint{ 2304 {Value: "even", Type: zonepb.Constraint_REQUIRED}, 2305 }, 2306 NumReplicas: 1, 2307 }, 2308 } 2309 2310 twoSpecificLocalities = []zonepb.ConstraintsConjunction{ 2311 { 2312 Constraints: []zonepb.Constraint{ 2313 {Key: "datacenter", Value: "a", Type: zonepb.Constraint_REQUIRED}, 2314 }, 2315 NumReplicas: 1, 2316 }, 2317 { 2318 Constraints: []zonepb.Constraint{ 2319 {Key: "datacenter", Value: "b", Type: zonepb.Constraint_REQUIRED}, 2320 }, 2321 NumReplicas: 1, 2322 }, 2323 } 2324 ) 2325 2326 func TestAllocateCandidatesNumReplicasConstraints(t *testing.T) { 2327 defer leaktest.AfterTest(t)() 2328 2329 stopper, g, _, a, _ := createTestAllocator(10, false /* deterministic */) 2330 defer stopper.Stop(context.Background()) 2331 sg := gossiputil.NewStoreGossiper(g) 2332 sg.GossipStores(multiDiversityDCStores, t) 2333 sl, _, _ := a.storePool.getStoreList(storeFilterThrottled) 2334 2335 // Given a set of existing replicas for a range, rank which of the remaining 2336 // stores from multiDiversityDCStores would be the best addition to the range 2337 // purely on the basis of constraint satisfaction and locality diversity. 2338 testCases := []struct { 2339 constraints []zonepb.ConstraintsConjunction 2340 existing []roachpb.StoreID 2341 expected []roachpb.StoreID 2342 }{ 2343 { 2344 threeSpecificLocalities, 2345 []roachpb.StoreID{}, 2346 []roachpb.StoreID{1, 2, 3, 4, 5, 6}, 2347 }, 2348 { 2349 threeSpecificLocalities, 2350 []roachpb.StoreID{7, 8}, 2351 []roachpb.StoreID{1, 2, 3, 4, 5, 6}, 2352 }, 2353 { 2354 threeSpecificLocalities, 2355 []roachpb.StoreID{1}, 2356 []roachpb.StoreID{3, 4, 5, 6}, 2357 }, 2358 { 2359 threeSpecificLocalities, 2360 []roachpb.StoreID{3, 5}, 2361 []roachpb.StoreID{1, 2}, 2362 }, 2363 { 2364 threeSpecificLocalities, 2365 []roachpb.StoreID{3, 4}, 2366 []roachpb.StoreID{1, 2, 5, 6}, 2367 }, 2368 { 2369 threeSpecificLocalities, 2370 []roachpb.StoreID{1, 3, 5}, 2371 []roachpb.StoreID{2, 4, 6}, 2372 }, 2373 { 2374 twoAndOneLocalities, 2375 []roachpb.StoreID{}, 2376 []roachpb.StoreID{1, 2, 3, 4}, 2377 }, 2378 { 2379 twoAndOneLocalities, 2380 []roachpb.StoreID{1}, 2381 []roachpb.StoreID{3, 4}, // 2 isn't included because its diversity is worse 2382 }, 2383 { 2384 twoAndOneLocalities, 2385 []roachpb.StoreID{1, 2}, 2386 []roachpb.StoreID{3, 4}, 2387 }, 2388 { 2389 twoAndOneLocalities, 2390 []roachpb.StoreID{1, 2, 3}, 2391 []roachpb.StoreID{4}, 2392 }, 2393 { 2394 twoAndOneLocalities, 2395 []roachpb.StoreID{3}, 2396 []roachpb.StoreID{1, 2}, 2397 }, 2398 { 2399 twoAndOneLocalities, 2400 []roachpb.StoreID{5}, 2401 []roachpb.StoreID{1, 2, 3, 4}, 2402 }, 2403 { 2404 threeInOneLocality, 2405 []roachpb.StoreID{}, 2406 []roachpb.StoreID{1, 2}, 2407 }, 2408 { 2409 threeInOneLocality, 2410 []roachpb.StoreID{3, 4, 5}, 2411 []roachpb.StoreID{1, 2}, 2412 }, 2413 { 2414 threeInOneLocality, 2415 []roachpb.StoreID{1}, 2416 []roachpb.StoreID{2}, 2417 }, 2418 { 2419 threeInOneLocality, 2420 []roachpb.StoreID{1, 2}, 2421 []roachpb.StoreID{}, 2422 }, 2423 { 2424 twoAndOneNodeAttrs, 2425 []roachpb.StoreID{}, 2426 []roachpb.StoreID{1, 2, 3, 4, 5, 6, 7, 8}, 2427 }, 2428 { 2429 twoAndOneNodeAttrs, 2430 []roachpb.StoreID{2}, 2431 []roachpb.StoreID{3, 5, 7}, 2432 }, 2433 { 2434 twoAndOneNodeAttrs, 2435 []roachpb.StoreID{1}, 2436 []roachpb.StoreID{3, 4, 5, 6, 7, 8}, 2437 }, 2438 { 2439 twoAndOneNodeAttrs, 2440 []roachpb.StoreID{1, 2}, 2441 []roachpb.StoreID{3, 5, 7}, 2442 }, 2443 { 2444 twoAndOneNodeAttrs, 2445 []roachpb.StoreID{1, 3}, 2446 []roachpb.StoreID{6, 8}, 2447 }, 2448 { 2449 twoAndOneNodeAttrs, 2450 []roachpb.StoreID{1, 3, 6}, 2451 []roachpb.StoreID{7, 8}, 2452 }, 2453 { 2454 twoAndOneStoreAttrs, 2455 []roachpb.StoreID{}, 2456 []roachpb.StoreID{1, 2, 3, 4, 5, 6, 7, 8}, 2457 }, 2458 { 2459 twoAndOneStoreAttrs, 2460 []roachpb.StoreID{2}, 2461 []roachpb.StoreID{3, 5, 7}, 2462 }, 2463 { 2464 twoAndOneStoreAttrs, 2465 []roachpb.StoreID{1}, 2466 []roachpb.StoreID{3, 4, 5, 6, 7, 8}, 2467 }, 2468 { 2469 twoAndOneStoreAttrs, 2470 []roachpb.StoreID{1, 2}, 2471 []roachpb.StoreID{3, 5, 7}, 2472 }, 2473 { 2474 twoAndOneStoreAttrs, 2475 []roachpb.StoreID{1, 3}, 2476 []roachpb.StoreID{6, 8}, 2477 }, 2478 { 2479 twoAndOneStoreAttrs, 2480 []roachpb.StoreID{1, 3, 6}, 2481 []roachpb.StoreID{7, 8}, 2482 }, 2483 { 2484 mixLocalityAndAttrs, 2485 []roachpb.StoreID{}, 2486 []roachpb.StoreID{1, 2, 3, 4, 6, 8}, 2487 }, 2488 { 2489 mixLocalityAndAttrs, 2490 []roachpb.StoreID{1}, 2491 []roachpb.StoreID{3, 4, 6, 8}, 2492 }, 2493 { 2494 mixLocalityAndAttrs, 2495 []roachpb.StoreID{2}, 2496 []roachpb.StoreID{3}, 2497 }, 2498 { 2499 mixLocalityAndAttrs, 2500 []roachpb.StoreID{3}, 2501 []roachpb.StoreID{1, 2, 6, 8}, 2502 }, 2503 { 2504 mixLocalityAndAttrs, 2505 []roachpb.StoreID{2, 3}, 2506 []roachpb.StoreID{1}, 2507 }, 2508 { 2509 mixLocalityAndAttrs, 2510 []roachpb.StoreID{1, 2}, 2511 []roachpb.StoreID{3}, 2512 }, 2513 { 2514 mixLocalityAndAttrs, 2515 []roachpb.StoreID{1, 3}, 2516 []roachpb.StoreID{6, 8}, 2517 }, 2518 { 2519 mixLocalityAndAttrs, 2520 []roachpb.StoreID{1, 2, 3}, 2521 []roachpb.StoreID{6, 8}, 2522 }, 2523 } 2524 2525 for testIdx, tc := range testCases { 2526 existingRepls := make([]roachpb.ReplicaDescriptor, len(tc.existing)) 2527 for i, storeID := range tc.existing { 2528 existingRepls[i] = roachpb.ReplicaDescriptor{ 2529 NodeID: roachpb.NodeID(storeID), 2530 StoreID: storeID, 2531 } 2532 } 2533 zone := &zonepb.ZoneConfig{NumReplicas: proto.Int32(0), Constraints: tc.constraints} 2534 analyzed := constraint.AnalyzeConstraints( 2535 context.Background(), a.storePool.getStoreDescriptor, existingRepls, zone) 2536 candidates := allocateCandidates( 2537 sl, 2538 analyzed, 2539 existingRepls, 2540 a.storePool.getLocalities(existingRepls), 2541 a.scorerOptions(), 2542 ) 2543 best := candidates.best() 2544 match := true 2545 if len(tc.expected) != len(best) { 2546 match = false 2547 } else { 2548 sort.Slice(best, func(i, j int) bool { 2549 return best[i].store.StoreID < best[j].store.StoreID 2550 }) 2551 for i := range tc.expected { 2552 if tc.expected[i] != best[i].store.StoreID { 2553 match = false 2554 break 2555 } 2556 } 2557 } 2558 if !match { 2559 t.Errorf("%d: expected allocateCandidates(%v) = %v, but got %v", 2560 testIdx, tc.existing, tc.expected, candidates) 2561 } 2562 } 2563 } 2564 2565 func TestRemoveCandidatesNumReplicasConstraints(t *testing.T) { 2566 defer leaktest.AfterTest(t)() 2567 2568 stopper, g, _, a, _ := createTestAllocator(10, false /* deterministic */) 2569 defer stopper.Stop(context.Background()) 2570 sg := gossiputil.NewStoreGossiper(g) 2571 sg.GossipStores(multiDiversityDCStores, t) 2572 2573 // Given a set of existing replicas for a range, rank which of the remaining 2574 // stores would be best to remove if we had to remove one purely on the basis 2575 // of constraint-matching and locality diversity. 2576 testCases := []struct { 2577 constraints []zonepb.ConstraintsConjunction 2578 existing []roachpb.StoreID 2579 expected []roachpb.StoreID 2580 }{ 2581 { 2582 threeSpecificLocalities, 2583 []roachpb.StoreID{1}, 2584 []roachpb.StoreID{1}, 2585 }, 2586 { 2587 threeSpecificLocalities, 2588 []roachpb.StoreID{1, 2}, 2589 []roachpb.StoreID{1, 2}, 2590 }, 2591 { 2592 threeSpecificLocalities, 2593 []roachpb.StoreID{1, 3}, 2594 []roachpb.StoreID{1, 3}, 2595 }, 2596 { 2597 threeSpecificLocalities, 2598 []roachpb.StoreID{1, 2, 3}, 2599 []roachpb.StoreID{1, 2}, 2600 }, 2601 { 2602 threeSpecificLocalities, 2603 []roachpb.StoreID{1, 3, 5}, 2604 []roachpb.StoreID{1, 3, 5}, 2605 }, 2606 { 2607 threeSpecificLocalities, 2608 []roachpb.StoreID{1, 3, 7}, 2609 []roachpb.StoreID{7}, 2610 }, 2611 { 2612 twoAndOneLocalities, 2613 []roachpb.StoreID{1, 3}, 2614 []roachpb.StoreID{1, 3}, 2615 }, 2616 { 2617 twoAndOneLocalities, 2618 []roachpb.StoreID{1, 3, 5}, 2619 []roachpb.StoreID{5}, 2620 }, 2621 { 2622 twoAndOneLocalities, 2623 []roachpb.StoreID{1, 3, 4}, 2624 []roachpb.StoreID{3, 4}, 2625 }, 2626 { 2627 twoAndOneLocalities, 2628 []roachpb.StoreID{1, 2, 3}, 2629 []roachpb.StoreID{1, 2}, 2630 }, 2631 { 2632 threeInOneLocality, 2633 []roachpb.StoreID{1, 3}, 2634 []roachpb.StoreID{3}, 2635 }, 2636 { 2637 threeInOneLocality, 2638 []roachpb.StoreID{2, 3}, 2639 []roachpb.StoreID{3}, 2640 }, 2641 { 2642 threeInOneLocality, 2643 []roachpb.StoreID{1, 2, 3}, 2644 []roachpb.StoreID{3}, 2645 }, 2646 { 2647 threeInOneLocality, 2648 []roachpb.StoreID{3, 5, 7}, 2649 []roachpb.StoreID{3, 5, 7}, 2650 }, 2651 { 2652 threeInOneLocality, 2653 []roachpb.StoreID{1, 2, 3, 5, 7}, 2654 []roachpb.StoreID{3, 5, 7}, 2655 }, 2656 { 2657 twoAndOneNodeAttrs, 2658 []roachpb.StoreID{1, 3}, 2659 []roachpb.StoreID{1, 3}, 2660 }, 2661 { 2662 twoAndOneNodeAttrs, 2663 []roachpb.StoreID{1, 2, 3}, 2664 []roachpb.StoreID{1, 2}, 2665 }, 2666 { 2667 twoAndOneNodeAttrs, 2668 []roachpb.StoreID{1, 3, 6}, 2669 []roachpb.StoreID{1, 3, 6}, 2670 }, 2671 { 2672 twoAndOneNodeAttrs, 2673 []roachpb.StoreID{1, 4, 6}, 2674 []roachpb.StoreID{4, 6}, 2675 }, 2676 { 2677 twoAndOneNodeAttrs, 2678 []roachpb.StoreID{1, 2, 6}, 2679 []roachpb.StoreID{2}, 2680 }, 2681 { 2682 twoAndOneStoreAttrs, 2683 []roachpb.StoreID{1, 2, 3}, 2684 []roachpb.StoreID{1, 2}, 2685 }, 2686 { 2687 twoAndOneStoreAttrs, 2688 []roachpb.StoreID{1, 3, 6}, 2689 []roachpb.StoreID{1, 3, 6}, 2690 }, 2691 { 2692 twoAndOneStoreAttrs, 2693 []roachpb.StoreID{1, 4, 6}, 2694 []roachpb.StoreID{4, 6}, 2695 }, 2696 { 2697 twoAndOneStoreAttrs, 2698 []roachpb.StoreID{1, 2, 6}, 2699 []roachpb.StoreID{2}, 2700 }, 2701 { 2702 mixLocalityAndAttrs, 2703 []roachpb.StoreID{1, 3, 6}, 2704 []roachpb.StoreID{1, 3, 6}, 2705 }, 2706 { 2707 mixLocalityAndAttrs, 2708 []roachpb.StoreID{1, 2, 3}, 2709 []roachpb.StoreID{1, 2}, 2710 }, 2711 { 2712 mixLocalityAndAttrs, 2713 []roachpb.StoreID{2, 3, 6}, 2714 []roachpb.StoreID{2, 6}, 2715 }, 2716 { 2717 mixLocalityAndAttrs, 2718 []roachpb.StoreID{2, 3, 4}, 2719 []roachpb.StoreID{4}, 2720 }, 2721 { 2722 mixLocalityAndAttrs, 2723 []roachpb.StoreID{5, 7}, 2724 []roachpb.StoreID{5, 7}, 2725 }, 2726 { 2727 // TODO(a-robinson): Should we prefer just 5 here for diversity reasons? 2728 // We'd have to rework our handling of invalid stores in a handful of 2729 // places, including in `candidateList.worst()`, to consider traits beyond 2730 // just invalidity. 2731 mixLocalityAndAttrs, 2732 []roachpb.StoreID{5, 6, 7}, 2733 []roachpb.StoreID{5, 7}, 2734 }, 2735 { 2736 mixLocalityAndAttrs, 2737 []roachpb.StoreID{1, 5, 7}, 2738 []roachpb.StoreID{5, 7}, 2739 }, 2740 { 2741 mixLocalityAndAttrs, 2742 []roachpb.StoreID{1, 6, 8}, 2743 []roachpb.StoreID{6, 8}, 2744 }, 2745 } 2746 2747 for testIdx, tc := range testCases { 2748 sl, _, _ := a.storePool.getStoreListFromIDs(tc.existing, storeFilterNone) 2749 existingRepls := make([]roachpb.ReplicaDescriptor, len(tc.existing)) 2750 for i, storeID := range tc.existing { 2751 existingRepls[i] = roachpb.ReplicaDescriptor{ 2752 NodeID: roachpb.NodeID(storeID), 2753 StoreID: storeID, 2754 } 2755 } 2756 zone := &zonepb.ZoneConfig{NumReplicas: proto.Int32(0), Constraints: tc.constraints} 2757 analyzed := constraint.AnalyzeConstraints( 2758 context.Background(), a.storePool.getStoreDescriptor, existingRepls, zone) 2759 candidates := removeCandidates( 2760 sl, 2761 analyzed, 2762 a.storePool.getLocalities(existingRepls), 2763 a.scorerOptions(), 2764 ) 2765 if !expectedStoreIDsMatch(tc.expected, candidates.worst()) { 2766 t.Errorf("%d: expected removeCandidates(%v) = %v, but got %v", 2767 testIdx, tc.existing, tc.expected, candidates) 2768 } 2769 } 2770 } 2771 2772 func expectedStoreIDsMatch(expected []roachpb.StoreID, results candidateList) bool { 2773 if len(expected) != len(results) { 2774 return false 2775 } 2776 sort.Slice(results, func(i, j int) bool { 2777 return results[i].store.StoreID < results[j].store.StoreID 2778 }) 2779 for i := range expected { 2780 if expected[i] != results[i].store.StoreID { 2781 return false 2782 } 2783 } 2784 return true 2785 } 2786 2787 func TestRebalanceCandidatesNumReplicasConstraints(t *testing.T) { 2788 defer leaktest.AfterTest(t)() 2789 2790 stopper, g, _, a, _ := createTestAllocator(10, false /* deterministic */) 2791 defer stopper.Stop(context.Background()) 2792 sg := gossiputil.NewStoreGossiper(g) 2793 sg.GossipStores(multiDiversityDCStores, t) 2794 sl, _, _ := a.storePool.getStoreList(storeFilterThrottled) 2795 2796 // Given a set of existing replicas for a range, rank which of the remaining 2797 // stores would be best to remove if we had to remove one purely on the basis 2798 // of constraint-matching and locality diversity. 2799 type rebalanceStoreIDs struct { 2800 existing []roachpb.StoreID 2801 candidates []roachpb.StoreID 2802 } 2803 testCases := []struct { 2804 constraints []zonepb.ConstraintsConjunction 2805 zoneNumReplicas int32 2806 existing []roachpb.StoreID 2807 expected []rebalanceStoreIDs 2808 validTargets []roachpb.StoreID 2809 }{ 2810 { 2811 constraints: threeSpecificLocalities, 2812 existing: []roachpb.StoreID{1}, 2813 expected: []rebalanceStoreIDs{}, // a store must be an improvement to justify rebalancing 2814 validTargets: []roachpb.StoreID{}, 2815 }, 2816 { 2817 constraints: threeSpecificLocalities, 2818 existing: []roachpb.StoreID{1, 3}, 2819 expected: []rebalanceStoreIDs{}, 2820 validTargets: []roachpb.StoreID{}, 2821 }, 2822 { 2823 constraints: threeSpecificLocalities, 2824 existing: []roachpb.StoreID{1, 3, 5}, 2825 expected: []rebalanceStoreIDs{}, 2826 validTargets: []roachpb.StoreID{}, 2827 }, 2828 { 2829 constraints: threeSpecificLocalities, 2830 existing: []roachpb.StoreID{1, 2}, 2831 expected: []rebalanceStoreIDs{ 2832 { 2833 existing: []roachpb.StoreID{1}, 2834 candidates: []roachpb.StoreID{3, 4, 5, 6}, 2835 }, 2836 { 2837 existing: []roachpb.StoreID{2}, 2838 candidates: []roachpb.StoreID{3, 4, 5, 6}, 2839 }, 2840 }, 2841 validTargets: []roachpb.StoreID{3, 4, 5, 6}, 2842 }, 2843 { 2844 constraints: threeSpecificLocalities, 2845 existing: []roachpb.StoreID{1, 2, 3}, 2846 expected: []rebalanceStoreIDs{ 2847 { 2848 existing: []roachpb.StoreID{1}, 2849 candidates: []roachpb.StoreID{5, 6}, 2850 }, 2851 { 2852 existing: []roachpb.StoreID{2}, 2853 candidates: []roachpb.StoreID{5, 6}, 2854 }, 2855 }, 2856 validTargets: []roachpb.StoreID{5, 6}, 2857 }, 2858 { 2859 constraints: threeSpecificLocalities, 2860 existing: []roachpb.StoreID{1, 3, 7}, 2861 expected: []rebalanceStoreIDs{ 2862 { 2863 existing: []roachpb.StoreID{7}, 2864 candidates: []roachpb.StoreID{5, 6}, 2865 }, 2866 }, 2867 validTargets: []roachpb.StoreID{5, 6}, 2868 }, 2869 { 2870 constraints: threeSpecificLocalities, 2871 existing: []roachpb.StoreID{1, 2, 7}, 2872 expected: []rebalanceStoreIDs{ 2873 { 2874 existing: []roachpb.StoreID{1}, 2875 candidates: []roachpb.StoreID{3, 4, 5, 6}, 2876 }, 2877 { 2878 existing: []roachpb.StoreID{2}, 2879 candidates: []roachpb.StoreID{3, 4, 5, 6}, 2880 }, 2881 { 2882 existing: []roachpb.StoreID{7}, 2883 candidates: []roachpb.StoreID{3, 4, 5, 6}, 2884 }, 2885 }, 2886 validTargets: []roachpb.StoreID{3, 4, 5, 6}, 2887 }, 2888 { 2889 constraints: threeSpecificLocalities, 2890 existing: []roachpb.StoreID{1, 7, 8}, 2891 expected: []rebalanceStoreIDs{ 2892 { 2893 existing: []roachpb.StoreID{7}, 2894 candidates: []roachpb.StoreID{3, 4, 5, 6}, 2895 }, 2896 { 2897 existing: []roachpb.StoreID{8}, 2898 candidates: []roachpb.StoreID{3, 4, 5, 6}, 2899 }, 2900 }, 2901 validTargets: []roachpb.StoreID{3, 4, 5, 6}, 2902 }, 2903 { 2904 constraints: twoAndOneLocalities, 2905 existing: []roachpb.StoreID{1, 2, 3}, 2906 expected: []rebalanceStoreIDs{}, 2907 validTargets: []roachpb.StoreID{}, 2908 }, 2909 { 2910 constraints: twoAndOneLocalities, 2911 existing: []roachpb.StoreID{2, 3, 4}, 2912 expected: []rebalanceStoreIDs{ 2913 { 2914 existing: []roachpb.StoreID{3}, 2915 candidates: []roachpb.StoreID{1}, 2916 }, 2917 { 2918 existing: []roachpb.StoreID{4}, 2919 candidates: []roachpb.StoreID{1}, 2920 }, 2921 }, 2922 validTargets: []roachpb.StoreID{1}, 2923 }, 2924 { 2925 constraints: twoAndOneLocalities, 2926 existing: []roachpb.StoreID{1, 2, 5}, 2927 expected: []rebalanceStoreIDs{ 2928 { 2929 existing: []roachpb.StoreID{1}, 2930 candidates: []roachpb.StoreID{3, 4}, 2931 }, 2932 { 2933 existing: []roachpb.StoreID{2}, 2934 candidates: []roachpb.StoreID{3, 4}, 2935 }, 2936 { 2937 existing: []roachpb.StoreID{5}, 2938 candidates: []roachpb.StoreID{3, 4}, 2939 }, 2940 }, 2941 validTargets: []roachpb.StoreID{3, 4}, 2942 }, 2943 { 2944 constraints: twoAndOneLocalities, 2945 existing: []roachpb.StoreID{1, 3, 5}, 2946 expected: []rebalanceStoreIDs{ 2947 { 2948 existing: []roachpb.StoreID{5}, 2949 candidates: []roachpb.StoreID{2}, 2950 }, 2951 }, 2952 validTargets: []roachpb.StoreID{2}, 2953 }, 2954 { 2955 constraints: twoAndOneLocalities, 2956 existing: []roachpb.StoreID{1, 5, 6}, 2957 expected: []rebalanceStoreIDs{ 2958 { 2959 existing: []roachpb.StoreID{5}, 2960 candidates: []roachpb.StoreID{3, 4}, 2961 }, 2962 { 2963 existing: []roachpb.StoreID{6}, 2964 candidates: []roachpb.StoreID{3, 4}, 2965 }, 2966 }, 2967 validTargets: []roachpb.StoreID{3, 4}, 2968 }, 2969 { 2970 constraints: twoAndOneLocalities, 2971 existing: []roachpb.StoreID{3, 5, 6}, 2972 expected: []rebalanceStoreIDs{ 2973 { 2974 existing: []roachpb.StoreID{5}, 2975 candidates: []roachpb.StoreID{1, 2}, 2976 }, 2977 { 2978 existing: []roachpb.StoreID{6}, 2979 candidates: []roachpb.StoreID{1, 2}, 2980 }, 2981 }, 2982 validTargets: []roachpb.StoreID{1, 2}, 2983 }, 2984 { 2985 constraints: twoAndOneLocalities, 2986 existing: []roachpb.StoreID{1, 3, 4}, 2987 expected: []rebalanceStoreIDs{ 2988 { 2989 existing: []roachpb.StoreID{3}, 2990 candidates: []roachpb.StoreID{2}, 2991 }, 2992 { 2993 existing: []roachpb.StoreID{4}, 2994 candidates: []roachpb.StoreID{2}, 2995 }, 2996 }, 2997 validTargets: []roachpb.StoreID{2}, 2998 }, 2999 { 3000 constraints: threeInOneLocality, 3001 existing: []roachpb.StoreID{1, 2, 3}, 3002 expected: []rebalanceStoreIDs{}, 3003 validTargets: []roachpb.StoreID{}, 3004 }, 3005 { 3006 constraints: threeInOneLocality, 3007 existing: []roachpb.StoreID{1, 3, 4}, 3008 expected: []rebalanceStoreIDs{ 3009 { 3010 existing: []roachpb.StoreID{3}, 3011 candidates: []roachpb.StoreID{2}, 3012 }, 3013 { 3014 existing: []roachpb.StoreID{4}, 3015 candidates: []roachpb.StoreID{2}, 3016 }, 3017 }, 3018 validTargets: []roachpb.StoreID{2}, 3019 }, 3020 { 3021 constraints: threeInOneLocality, 3022 existing: []roachpb.StoreID{3, 4, 5}, 3023 expected: []rebalanceStoreIDs{ 3024 { 3025 existing: []roachpb.StoreID{3}, 3026 candidates: []roachpb.StoreID{1, 2}, 3027 }, 3028 { 3029 existing: []roachpb.StoreID{4}, 3030 candidates: []roachpb.StoreID{1, 2}, 3031 }, 3032 { 3033 existing: []roachpb.StoreID{5}, 3034 candidates: []roachpb.StoreID{1, 2}, 3035 }, 3036 }, 3037 validTargets: []roachpb.StoreID{1, 2}, 3038 }, 3039 { 3040 constraints: twoAndOneNodeAttrs, 3041 existing: []roachpb.StoreID{1, 4, 5}, 3042 expected: []rebalanceStoreIDs{}, 3043 validTargets: []roachpb.StoreID{}, 3044 }, 3045 { 3046 constraints: twoAndOneNodeAttrs, 3047 existing: []roachpb.StoreID{3, 6, 7}, 3048 expected: []rebalanceStoreIDs{}, 3049 validTargets: []roachpb.StoreID{}, 3050 }, 3051 { 3052 constraints: twoAndOneNodeAttrs, 3053 existing: []roachpb.StoreID{1, 2, 3}, 3054 expected: []rebalanceStoreIDs{ 3055 { 3056 existing: []roachpb.StoreID{1}, 3057 candidates: []roachpb.StoreID{5, 7}, 3058 }, 3059 { 3060 existing: []roachpb.StoreID{2}, 3061 candidates: []roachpb.StoreID{6, 8}, 3062 }, 3063 }, 3064 validTargets: []roachpb.StoreID{5, 6, 7, 8}, 3065 }, 3066 { 3067 constraints: twoAndOneNodeAttrs, 3068 existing: []roachpb.StoreID{2, 3, 4}, 3069 expected: []rebalanceStoreIDs{ 3070 { 3071 existing: []roachpb.StoreID{2}, 3072 candidates: []roachpb.StoreID{1, 5, 7}, 3073 }, 3074 { 3075 existing: []roachpb.StoreID{3}, 3076 candidates: []roachpb.StoreID{5, 7}, 3077 }, 3078 { 3079 existing: []roachpb.StoreID{4}, 3080 candidates: []roachpb.StoreID{5, 7}, 3081 }, 3082 }, 3083 validTargets: []roachpb.StoreID{5, 7}, 3084 }, 3085 { 3086 constraints: twoAndOneNodeAttrs, 3087 existing: []roachpb.StoreID{2, 4, 5}, 3088 expected: []rebalanceStoreIDs{ 3089 { 3090 existing: []roachpb.StoreID{2}, 3091 candidates: []roachpb.StoreID{1, 7}, 3092 }, 3093 { 3094 existing: []roachpb.StoreID{4}, 3095 candidates: []roachpb.StoreID{3, 7}, 3096 }, 3097 }, 3098 validTargets: []roachpb.StoreID{1, 3, 7}, 3099 }, 3100 { 3101 constraints: twoAndOneNodeAttrs, 3102 existing: []roachpb.StoreID{1, 3, 5}, 3103 expected: []rebalanceStoreIDs{ 3104 { 3105 existing: []roachpb.StoreID{1}, 3106 candidates: []roachpb.StoreID{2, 8}, 3107 }, 3108 { 3109 existing: []roachpb.StoreID{3}, 3110 candidates: []roachpb.StoreID{4, 8}, 3111 }, 3112 { 3113 existing: []roachpb.StoreID{5}, 3114 candidates: []roachpb.StoreID{6, 8}, 3115 }, 3116 }, 3117 validTargets: []roachpb.StoreID{2, 4, 6, 8}, 3118 }, 3119 { 3120 constraints: twoAndOneNodeAttrs, 3121 existing: []roachpb.StoreID{2, 4, 6}, 3122 expected: []rebalanceStoreIDs{ 3123 { 3124 existing: []roachpb.StoreID{2}, 3125 candidates: []roachpb.StoreID{1, 7}, 3126 }, 3127 { 3128 existing: []roachpb.StoreID{4}, 3129 candidates: []roachpb.StoreID{3, 7}, 3130 }, 3131 { 3132 existing: []roachpb.StoreID{6}, 3133 candidates: []roachpb.StoreID{5, 7}, 3134 }, 3135 }, 3136 validTargets: []roachpb.StoreID{1, 3, 5, 7}, 3137 }, 3138 { 3139 constraints: twoAndOneStoreAttrs, 3140 existing: []roachpb.StoreID{1, 4, 5}, 3141 expected: []rebalanceStoreIDs{}, 3142 validTargets: []roachpb.StoreID{}, 3143 }, 3144 { 3145 constraints: twoAndOneStoreAttrs, 3146 existing: []roachpb.StoreID{3, 6, 7}, 3147 expected: []rebalanceStoreIDs{}, 3148 validTargets: []roachpb.StoreID{}, 3149 }, 3150 { 3151 constraints: twoAndOneStoreAttrs, 3152 existing: []roachpb.StoreID{1, 2, 3}, 3153 expected: []rebalanceStoreIDs{ 3154 { 3155 existing: []roachpb.StoreID{1}, 3156 candidates: []roachpb.StoreID{5, 7}, 3157 }, 3158 { 3159 existing: []roachpb.StoreID{2}, 3160 candidates: []roachpb.StoreID{6, 8}, 3161 }, 3162 }, 3163 validTargets: []roachpb.StoreID{5, 6, 7, 8}, 3164 }, 3165 { 3166 constraints: twoAndOneStoreAttrs, 3167 existing: []roachpb.StoreID{2, 3, 4}, 3168 expected: []rebalanceStoreIDs{ 3169 { 3170 existing: []roachpb.StoreID{2}, 3171 candidates: []roachpb.StoreID{1, 5, 7}, 3172 }, 3173 { 3174 existing: []roachpb.StoreID{3}, 3175 candidates: []roachpb.StoreID{5, 7}, 3176 }, 3177 { 3178 existing: []roachpb.StoreID{4}, 3179 candidates: []roachpb.StoreID{5, 7}, 3180 }, 3181 }, 3182 validTargets: []roachpb.StoreID{5, 7}, 3183 }, 3184 { 3185 constraints: twoAndOneStoreAttrs, 3186 existing: []roachpb.StoreID{2, 4, 5}, 3187 expected: []rebalanceStoreIDs{ 3188 { 3189 existing: []roachpb.StoreID{2}, 3190 candidates: []roachpb.StoreID{1, 7}, 3191 }, 3192 { 3193 existing: []roachpb.StoreID{4}, 3194 candidates: []roachpb.StoreID{3, 7}, 3195 }, 3196 }, 3197 validTargets: []roachpb.StoreID{1, 3, 7}, 3198 }, 3199 { 3200 constraints: twoAndOneStoreAttrs, 3201 existing: []roachpb.StoreID{1, 3, 5}, 3202 expected: []rebalanceStoreIDs{ 3203 { 3204 existing: []roachpb.StoreID{1}, 3205 candidates: []roachpb.StoreID{2, 8}, 3206 }, 3207 { 3208 existing: []roachpb.StoreID{3}, 3209 candidates: []roachpb.StoreID{4, 8}, 3210 }, 3211 { 3212 existing: []roachpb.StoreID{5}, 3213 candidates: []roachpb.StoreID{6, 8}, 3214 }, 3215 }, 3216 validTargets: []roachpb.StoreID{2, 4, 6, 8}, 3217 }, 3218 { 3219 constraints: twoAndOneStoreAttrs, 3220 existing: []roachpb.StoreID{2, 4, 6}, 3221 expected: []rebalanceStoreIDs{ 3222 { 3223 existing: []roachpb.StoreID{2}, 3224 candidates: []roachpb.StoreID{1, 7}, 3225 }, 3226 { 3227 existing: []roachpb.StoreID{4}, 3228 candidates: []roachpb.StoreID{3, 7}, 3229 }, 3230 { 3231 existing: []roachpb.StoreID{6}, 3232 candidates: []roachpb.StoreID{5, 7}, 3233 }, 3234 }, 3235 validTargets: []roachpb.StoreID{1, 3, 5, 7}, 3236 }, 3237 { 3238 constraints: mixLocalityAndAttrs, 3239 existing: []roachpb.StoreID{1, 3, 6}, 3240 expected: []rebalanceStoreIDs{}, 3241 validTargets: []roachpb.StoreID{}, 3242 }, 3243 { 3244 constraints: mixLocalityAndAttrs, 3245 existing: []roachpb.StoreID{1, 3, 8}, 3246 expected: []rebalanceStoreIDs{}, 3247 validTargets: []roachpb.StoreID{}, 3248 }, 3249 { 3250 constraints: mixLocalityAndAttrs, 3251 existing: []roachpb.StoreID{1, 5, 8}, 3252 expected: []rebalanceStoreIDs{ 3253 { 3254 existing: []roachpb.StoreID{5}, 3255 candidates: []roachpb.StoreID{3}, 3256 }, 3257 }, 3258 validTargets: []roachpb.StoreID{3}, 3259 }, 3260 { 3261 constraints: mixLocalityAndAttrs, 3262 existing: []roachpb.StoreID{1, 5, 6}, 3263 expected: []rebalanceStoreIDs{ 3264 { 3265 existing: []roachpb.StoreID{5}, 3266 candidates: []roachpb.StoreID{3}, 3267 }, 3268 { 3269 existing: []roachpb.StoreID{6}, 3270 candidates: []roachpb.StoreID{3, 4, 8}, 3271 }, 3272 }, 3273 validTargets: []roachpb.StoreID{3}, 3274 }, 3275 { 3276 constraints: mixLocalityAndAttrs, 3277 existing: []roachpb.StoreID{1, 3, 5}, 3278 expected: []rebalanceStoreIDs{ 3279 { 3280 existing: []roachpb.StoreID{5}, 3281 candidates: []roachpb.StoreID{6, 8}, 3282 }, 3283 }, 3284 validTargets: []roachpb.StoreID{6, 8}, 3285 }, 3286 { 3287 constraints: mixLocalityAndAttrs, 3288 existing: []roachpb.StoreID{1, 2, 3}, 3289 expected: []rebalanceStoreIDs{ 3290 { 3291 existing: []roachpb.StoreID{2}, 3292 candidates: []roachpb.StoreID{6, 8}, 3293 }, 3294 }, 3295 validTargets: []roachpb.StoreID{6, 8}, 3296 }, 3297 { 3298 constraints: mixLocalityAndAttrs, 3299 existing: []roachpb.StoreID{1, 3, 4}, 3300 expected: []rebalanceStoreIDs{ 3301 { 3302 existing: []roachpb.StoreID{4}, 3303 candidates: []roachpb.StoreID{6, 8}, 3304 }, 3305 }, 3306 validTargets: []roachpb.StoreID{6, 8}, 3307 }, 3308 { 3309 constraints: mixLocalityAndAttrs, 3310 existing: []roachpb.StoreID{2, 3, 4}, 3311 expected: []rebalanceStoreIDs{ 3312 { 3313 existing: []roachpb.StoreID{2}, 3314 candidates: []roachpb.StoreID{1}, 3315 }, 3316 { 3317 existing: []roachpb.StoreID{4}, 3318 candidates: []roachpb.StoreID{1}, 3319 }, 3320 }, 3321 validTargets: []roachpb.StoreID{1}, 3322 }, 3323 { 3324 constraints: mixLocalityAndAttrs, 3325 existing: []roachpb.StoreID{5, 6, 7}, 3326 expected: []rebalanceStoreIDs{ 3327 { 3328 existing: []roachpb.StoreID{5}, 3329 candidates: []roachpb.StoreID{1, 3}, 3330 }, 3331 { 3332 existing: []roachpb.StoreID{6}, 3333 candidates: []roachpb.StoreID{1, 2, 3, 4}, 3334 }, 3335 { 3336 existing: []roachpb.StoreID{7}, 3337 candidates: []roachpb.StoreID{1, 3}, 3338 }, 3339 }, 3340 validTargets: []roachpb.StoreID{1, 3}, 3341 }, 3342 { 3343 constraints: mixLocalityAndAttrs, 3344 existing: []roachpb.StoreID{6, 7, 8}, 3345 expected: []rebalanceStoreIDs{ 3346 { 3347 existing: []roachpb.StoreID{6}, 3348 candidates: []roachpb.StoreID{1, 3}, 3349 }, 3350 { 3351 existing: []roachpb.StoreID{7}, 3352 candidates: []roachpb.StoreID{1, 3}, 3353 }, 3354 { 3355 existing: []roachpb.StoreID{8}, 3356 candidates: []roachpb.StoreID{1, 3}, 3357 }, 3358 }, 3359 validTargets: []roachpb.StoreID{1, 3}, 3360 }, 3361 { 3362 constraints: mixLocalityAndAttrs, 3363 existing: []roachpb.StoreID{1, 6, 8}, 3364 expected: []rebalanceStoreIDs{ 3365 { 3366 existing: []roachpb.StoreID{6}, 3367 candidates: []roachpb.StoreID{3}, 3368 }, 3369 { 3370 existing: []roachpb.StoreID{8}, 3371 candidates: []roachpb.StoreID{3}, 3372 }, 3373 }, 3374 validTargets: []roachpb.StoreID{3}, 3375 }, 3376 { 3377 constraints: mixLocalityAndAttrs, 3378 existing: []roachpb.StoreID{1, 5, 7}, 3379 expected: []rebalanceStoreIDs{ 3380 { 3381 existing: []roachpb.StoreID{5}, 3382 candidates: []roachpb.StoreID{3, 4, 6}, 3383 }, 3384 { 3385 existing: []roachpb.StoreID{7}, 3386 candidates: []roachpb.StoreID{3, 4, 8}, 3387 }, 3388 }, 3389 validTargets: []roachpb.StoreID{3, 4, 6, 8}, 3390 }, 3391 { 3392 constraints: twoSpecificLocalities, 3393 zoneNumReplicas: 3, 3394 existing: []roachpb.StoreID{1, 3, 5}, 3395 expected: []rebalanceStoreIDs{}, 3396 validTargets: []roachpb.StoreID{}, 3397 }, 3398 { 3399 constraints: twoSpecificLocalities, 3400 zoneNumReplicas: 3, 3401 existing: []roachpb.StoreID{1, 3, 7}, 3402 expected: []rebalanceStoreIDs{}, 3403 validTargets: []roachpb.StoreID{}, 3404 }, 3405 { 3406 constraints: twoSpecificLocalities, 3407 zoneNumReplicas: 3, 3408 existing: []roachpb.StoreID{2, 4, 8}, 3409 expected: []rebalanceStoreIDs{}, 3410 validTargets: []roachpb.StoreID{}, 3411 }, 3412 { 3413 constraints: twoSpecificLocalities, 3414 zoneNumReplicas: 3, 3415 existing: []roachpb.StoreID{1, 2, 3}, 3416 expected: []rebalanceStoreIDs{ 3417 { 3418 existing: []roachpb.StoreID{1}, 3419 candidates: []roachpb.StoreID{5, 6, 7, 8}, 3420 }, 3421 { 3422 existing: []roachpb.StoreID{2}, 3423 candidates: []roachpb.StoreID{5, 6, 7, 8}, 3424 }, 3425 }, 3426 validTargets: []roachpb.StoreID{5, 6, 7, 8}, 3427 }, 3428 { 3429 constraints: twoSpecificLocalities, 3430 zoneNumReplicas: 3, 3431 existing: []roachpb.StoreID{2, 3, 4}, 3432 expected: []rebalanceStoreIDs{ 3433 { 3434 existing: []roachpb.StoreID{3}, 3435 candidates: []roachpb.StoreID{5, 6, 7, 8}, 3436 }, 3437 { 3438 existing: []roachpb.StoreID{4}, 3439 candidates: []roachpb.StoreID{5, 6, 7, 8}, 3440 }, 3441 }, 3442 validTargets: []roachpb.StoreID{5, 6, 7, 8}, 3443 }, 3444 { 3445 constraints: twoSpecificLocalities, 3446 zoneNumReplicas: 3, 3447 existing: []roachpb.StoreID{1, 2, 5}, 3448 expected: []rebalanceStoreIDs{ 3449 { 3450 existing: []roachpb.StoreID{1}, 3451 candidates: []roachpb.StoreID{3, 4}, 3452 }, 3453 { 3454 existing: []roachpb.StoreID{2}, 3455 candidates: []roachpb.StoreID{3, 4}, 3456 }, 3457 { 3458 existing: []roachpb.StoreID{5}, 3459 candidates: []roachpb.StoreID{3, 4}, 3460 }, 3461 }, 3462 validTargets: []roachpb.StoreID{3, 4}, 3463 }, 3464 { 3465 constraints: twoSpecificLocalities, 3466 zoneNumReplicas: 3, 3467 existing: []roachpb.StoreID{3, 4, 5}, 3468 expected: []rebalanceStoreIDs{ 3469 { 3470 existing: []roachpb.StoreID{3}, 3471 candidates: []roachpb.StoreID{1, 2}, 3472 }, 3473 { 3474 existing: []roachpb.StoreID{4}, 3475 candidates: []roachpb.StoreID{1, 2}, 3476 }, 3477 { 3478 existing: []roachpb.StoreID{5}, 3479 candidates: []roachpb.StoreID{1, 2}, 3480 }, 3481 }, 3482 validTargets: []roachpb.StoreID{1, 2}, 3483 }, 3484 { 3485 constraints: twoSpecificLocalities, 3486 zoneNumReplicas: 3, 3487 existing: []roachpb.StoreID{1, 5, 7}, 3488 expected: []rebalanceStoreIDs{ 3489 { 3490 existing: []roachpb.StoreID{5}, 3491 candidates: []roachpb.StoreID{3, 4}, 3492 }, 3493 { 3494 existing: []roachpb.StoreID{7}, 3495 candidates: []roachpb.StoreID{3, 4}, 3496 }, 3497 }, 3498 validTargets: []roachpb.StoreID{3, 4}, 3499 }, 3500 { 3501 constraints: twoSpecificLocalities, 3502 zoneNumReplicas: 3, 3503 existing: []roachpb.StoreID{1, 5, 6}, 3504 expected: []rebalanceStoreIDs{ 3505 { 3506 existing: []roachpb.StoreID{5}, 3507 candidates: []roachpb.StoreID{3, 4}, 3508 }, 3509 { 3510 existing: []roachpb.StoreID{6}, 3511 candidates: []roachpb.StoreID{3, 4}, 3512 }, 3513 }, 3514 validTargets: []roachpb.StoreID{3, 4}, 3515 }, 3516 { 3517 constraints: twoSpecificLocalities, 3518 zoneNumReplicas: 3, 3519 existing: []roachpb.StoreID{5, 6, 7}, 3520 expected: []rebalanceStoreIDs{ 3521 { 3522 existing: []roachpb.StoreID{5}, 3523 candidates: []roachpb.StoreID{1, 2, 3, 4}, 3524 }, 3525 { 3526 existing: []roachpb.StoreID{6}, 3527 candidates: []roachpb.StoreID{1, 2, 3, 4}, 3528 }, 3529 { 3530 existing: []roachpb.StoreID{7}, 3531 candidates: []roachpb.StoreID{1, 2, 3, 4}, 3532 }, 3533 }, 3534 validTargets: []roachpb.StoreID{1, 2, 3, 4}, 3535 }, 3536 } 3537 3538 for testIdx, tc := range testCases { 3539 existingRepls := make([]roachpb.ReplicaDescriptor, len(tc.existing)) 3540 for i, storeID := range tc.existing { 3541 existingRepls[i] = roachpb.ReplicaDescriptor{ 3542 NodeID: roachpb.NodeID(storeID), 3543 StoreID: storeID, 3544 } 3545 } 3546 var rangeUsageInfo RangeUsageInfo 3547 zone := &zonepb.ZoneConfig{ 3548 Constraints: tc.constraints, 3549 NumReplicas: proto.Int32(tc.zoneNumReplicas), 3550 } 3551 analyzed := constraint.AnalyzeConstraints( 3552 context.Background(), a.storePool.getStoreDescriptor, existingRepls, zone) 3553 results := rebalanceCandidates( 3554 context.Background(), 3555 sl, 3556 analyzed, 3557 existingRepls, 3558 a.storePool.getLocalities(existingRepls), 3559 a.storePool.getNodeLocalityString, 3560 a.scorerOptions(), 3561 ) 3562 match := true 3563 if len(tc.expected) != len(results) { 3564 match = false 3565 } else { 3566 sort.Slice(results, func(i, j int) bool { 3567 return results[i].existingCandidates[0].store.StoreID < results[j].existingCandidates[0].store.StoreID 3568 }) 3569 for i := range tc.expected { 3570 if !expectedStoreIDsMatch(tc.expected[i].existing, results[i].existingCandidates) || 3571 !expectedStoreIDsMatch(tc.expected[i].candidates, results[i].candidates) { 3572 match = false 3573 break 3574 } 3575 } 3576 } 3577 if !match { 3578 t.Errorf("%d: expected rebalanceCandidates(%v) = %v, but got %v", 3579 testIdx, tc.existing, tc.expected, results) 3580 } else { 3581 // Also verify that RebalanceTarget picks out one of the best options as 3582 // the final rebalance choice. 3583 target, _, details, ok := a.RebalanceTarget( 3584 context.Background(), zone, nil, existingRepls, rangeUsageInfo, storeFilterThrottled) 3585 var found bool 3586 if !ok && len(tc.validTargets) == 0 { 3587 found = true 3588 } 3589 for _, storeID := range tc.validTargets { 3590 if storeID == target.StoreID { 3591 found = true 3592 break 3593 } 3594 } 3595 if !found { 3596 t.Errorf("%d: expected RebalanceTarget(%v) to be in %v, but got %v; details: %s", 3597 testIdx, tc.existing, tc.validTargets, target, details) 3598 } 3599 } 3600 } 3601 } 3602 3603 // Test out the load-based lease transfer algorithm against a variety of 3604 // request distributions and inter-node latencies. 3605 func TestAllocatorTransferLeaseTargetLoadBased(t *testing.T) { 3606 defer leaktest.AfterTest(t)() 3607 3608 stopper, g, _, storePool, _ := createTestStorePool( 3609 TestTimeUntilStoreDeadOff, true, /* deterministic */ 3610 func() int { return 10 }, /* nodeCount */ 3611 kvserverpb.NodeLivenessStatus_LIVE) 3612 defer stopper.Stop(context.Background()) 3613 3614 // 3 stores where the lease count for each store is equal to 10x the store ID. 3615 var stores []*roachpb.StoreDescriptor 3616 for i := 1; i <= 3; i++ { 3617 stores = append(stores, &roachpb.StoreDescriptor{ 3618 StoreID: roachpb.StoreID(i), 3619 Node: roachpb.NodeDescriptor{ 3620 NodeID: roachpb.NodeID(i), 3621 Address: util.MakeUnresolvedAddr("tcp", strconv.Itoa(i)), 3622 Locality: roachpb.Locality{ 3623 Tiers: []roachpb.Tier{ 3624 {Key: "l", Value: strconv.Itoa(i)}, 3625 }, 3626 }, 3627 }, 3628 Capacity: roachpb.StoreCapacity{LeaseCount: int32(10 * i)}, 3629 }) 3630 } 3631 sg := gossiputil.NewStoreGossiper(g) 3632 sg.GossipStores(stores, t) 3633 3634 // Nodes need to have descriptors in gossip for the load-based algorithm to 3635 // consider transferring a lease to them. 3636 for _, store := range stores { 3637 if err := g.SetNodeDescriptor(&store.Node); err != nil { 3638 t.Fatal(err) 3639 } 3640 } 3641 3642 localities := map[roachpb.NodeID]string{ 3643 1: "l=1", 3644 2: "l=2", 3645 3: "l=3", 3646 } 3647 localityFn := func(nodeID roachpb.NodeID) string { 3648 return localities[nodeID] 3649 } 3650 manual := hlc.NewManualClock(123) 3651 clock := hlc.NewClock(manual.UnixNano, time.Nanosecond) 3652 3653 // Set up four different load distributions. Record a bunch of requests to 3654 // the unknown node 99 in evenlyBalanced to verify that requests from 3655 // unknown localities don't affect the algorithm. 3656 evenlyBalanced := newReplicaStats(clock, localityFn) 3657 evenlyBalanced.record(1) 3658 evenlyBalanced.record(2) 3659 evenlyBalanced.record(3) 3660 imbalanced1 := newReplicaStats(clock, localityFn) 3661 imbalanced2 := newReplicaStats(clock, localityFn) 3662 imbalanced3 := newReplicaStats(clock, localityFn) 3663 for i := 0; i < 100*int(MinLeaseTransferStatsDuration.Seconds()); i++ { 3664 evenlyBalanced.record(99) 3665 imbalanced1.record(1) 3666 imbalanced2.record(2) 3667 imbalanced3.record(3) 3668 } 3669 3670 manual.Increment(int64(MinLeaseTransferStatsDuration)) 3671 3672 noLatency := map[string]time.Duration{} 3673 highLatency := map[string]time.Duration{ 3674 stores[0].Node.Address.String(): 50 * time.Millisecond, 3675 stores[1].Node.Address.String(): 50 * time.Millisecond, 3676 stores[2].Node.Address.String(): 50 * time.Millisecond, 3677 } 3678 3679 existing := []roachpb.ReplicaDescriptor{ 3680 {NodeID: 1, StoreID: 1}, 3681 {NodeID: 2, StoreID: 2}, 3682 {NodeID: 3, StoreID: 3}, 3683 } 3684 3685 testCases := []struct { 3686 leaseholder roachpb.StoreID 3687 latency map[string]time.Duration 3688 stats *replicaStats 3689 check bool 3690 expected roachpb.StoreID 3691 }{ 3692 // No existing lease holder, nothing to do. 3693 {leaseholder: 0, latency: noLatency, stats: evenlyBalanced, check: true, expected: 0}, 3694 {leaseholder: 1, latency: noLatency, stats: evenlyBalanced, check: true, expected: 0}, 3695 {leaseholder: 1, latency: noLatency, stats: evenlyBalanced, check: false, expected: 2}, 3696 {leaseholder: 2, latency: noLatency, stats: evenlyBalanced, check: true, expected: 1}, 3697 {leaseholder: 2, latency: noLatency, stats: evenlyBalanced, check: false, expected: 1}, 3698 {leaseholder: 3, latency: noLatency, stats: evenlyBalanced, check: true, expected: 1}, 3699 {leaseholder: 3, latency: noLatency, stats: evenlyBalanced, check: false, expected: 1}, 3700 {leaseholder: 0, latency: noLatency, stats: imbalanced1, check: true, expected: 0}, 3701 {leaseholder: 1, latency: noLatency, stats: imbalanced1, check: true, expected: 0}, 3702 {leaseholder: 1, latency: noLatency, stats: imbalanced1, check: false, expected: 2}, 3703 {leaseholder: 2, latency: noLatency, stats: imbalanced1, check: true, expected: 1}, 3704 {leaseholder: 2, latency: noLatency, stats: imbalanced1, check: false, expected: 1}, 3705 {leaseholder: 3, latency: noLatency, stats: imbalanced1, check: true, expected: 1}, 3706 {leaseholder: 3, latency: noLatency, stats: imbalanced1, check: false, expected: 1}, 3707 {leaseholder: 0, latency: noLatency, stats: imbalanced2, check: true, expected: 0}, 3708 {leaseholder: 1, latency: noLatency, stats: imbalanced2, check: true, expected: 0}, 3709 {leaseholder: 1, latency: noLatency, stats: imbalanced2, check: false, expected: 2}, 3710 {leaseholder: 2, latency: noLatency, stats: imbalanced2, check: true, expected: 1}, 3711 {leaseholder: 2, latency: noLatency, stats: imbalanced2, check: false, expected: 1}, 3712 {leaseholder: 3, latency: noLatency, stats: imbalanced2, check: true, expected: 1}, 3713 {leaseholder: 3, latency: noLatency, stats: imbalanced2, check: false, expected: 1}, 3714 {leaseholder: 0, latency: noLatency, stats: imbalanced3, check: true, expected: 0}, 3715 {leaseholder: 1, latency: noLatency, stats: imbalanced3, check: true, expected: 0}, 3716 {leaseholder: 1, latency: noLatency, stats: imbalanced3, check: false, expected: 2}, 3717 {leaseholder: 2, latency: noLatency, stats: imbalanced3, check: true, expected: 1}, 3718 {leaseholder: 2, latency: noLatency, stats: imbalanced3, check: false, expected: 1}, 3719 {leaseholder: 3, latency: noLatency, stats: imbalanced3, check: true, expected: 1}, 3720 {leaseholder: 3, latency: noLatency, stats: imbalanced3, check: false, expected: 1}, 3721 {leaseholder: 0, latency: highLatency, stats: evenlyBalanced, check: true, expected: 0}, 3722 {leaseholder: 1, latency: highLatency, stats: evenlyBalanced, check: true, expected: 0}, 3723 {leaseholder: 1, latency: highLatency, stats: evenlyBalanced, check: false, expected: 2}, 3724 {leaseholder: 2, latency: highLatency, stats: evenlyBalanced, check: true, expected: 1}, 3725 {leaseholder: 2, latency: highLatency, stats: evenlyBalanced, check: false, expected: 1}, 3726 {leaseholder: 3, latency: highLatency, stats: evenlyBalanced, check: true, expected: 1}, 3727 {leaseholder: 3, latency: highLatency, stats: evenlyBalanced, check: false, expected: 1}, 3728 {leaseholder: 0, latency: highLatency, stats: imbalanced1, check: true, expected: 0}, 3729 {leaseholder: 1, latency: highLatency, stats: imbalanced1, check: true, expected: 0}, 3730 {leaseholder: 1, latency: highLatency, stats: imbalanced1, check: false, expected: 2}, 3731 {leaseholder: 2, latency: highLatency, stats: imbalanced1, check: true, expected: 1}, 3732 {leaseholder: 2, latency: highLatency, stats: imbalanced1, check: false, expected: 1}, 3733 {leaseholder: 3, latency: highLatency, stats: imbalanced1, check: true, expected: 1}, 3734 {leaseholder: 3, latency: highLatency, stats: imbalanced1, check: false, expected: 1}, 3735 {leaseholder: 0, latency: highLatency, stats: imbalanced2, check: true, expected: 0}, 3736 {leaseholder: 1, latency: highLatency, stats: imbalanced2, check: true, expected: 2}, 3737 {leaseholder: 1, latency: highLatency, stats: imbalanced2, check: false, expected: 2}, 3738 {leaseholder: 2, latency: highLatency, stats: imbalanced2, check: true, expected: 0}, 3739 {leaseholder: 2, latency: highLatency, stats: imbalanced2, check: false, expected: 1}, 3740 {leaseholder: 3, latency: highLatency, stats: imbalanced2, check: true, expected: 2}, 3741 {leaseholder: 3, latency: highLatency, stats: imbalanced2, check: false, expected: 2}, 3742 {leaseholder: 0, latency: highLatency, stats: imbalanced3, check: true, expected: 0}, 3743 {leaseholder: 1, latency: highLatency, stats: imbalanced3, check: true, expected: 3}, 3744 {leaseholder: 1, latency: highLatency, stats: imbalanced3, check: false, expected: 3}, 3745 {leaseholder: 2, latency: highLatency, stats: imbalanced3, check: true, expected: 3}, 3746 {leaseholder: 2, latency: highLatency, stats: imbalanced3, check: false, expected: 3}, 3747 {leaseholder: 3, latency: highLatency, stats: imbalanced3, check: true, expected: 0}, 3748 {leaseholder: 3, latency: highLatency, stats: imbalanced3, check: false, expected: 1}, 3749 } 3750 3751 for _, c := range testCases { 3752 t.Run("", func(t *testing.T) { 3753 a := MakeAllocator(storePool, func(addr string) (time.Duration, bool) { 3754 return c.latency[addr], true 3755 }) 3756 target := a.TransferLeaseTarget( 3757 context.Background(), 3758 zonepb.EmptyCompleteZoneConfig(), 3759 existing, 3760 c.leaseholder, 3761 c.stats, 3762 c.check, 3763 true, /* checkCandidateFullness */ 3764 false, /* alwaysAllowDecisionWithoutStats */ 3765 ) 3766 if c.expected != target.StoreID { 3767 t.Errorf("expected %d, got %d", c.expected, target.StoreID) 3768 } 3769 }) 3770 } 3771 } 3772 3773 func TestLoadBasedLeaseRebalanceScore(t *testing.T) { 3774 defer leaktest.AfterTest(t)() 3775 3776 st := cluster.MakeTestingClusterSettings() 3777 enableLoadBasedLeaseRebalancing.Override(&st.SV, true) 3778 3779 remoteStore := roachpb.StoreDescriptor{ 3780 Node: roachpb.NodeDescriptor{ 3781 NodeID: 2, 3782 }, 3783 } 3784 sourceStore := roachpb.StoreDescriptor{ 3785 Node: roachpb.NodeDescriptor{ 3786 NodeID: 1, 3787 }, 3788 } 3789 3790 testCases := []struct { 3791 remoteWeight float64 3792 remoteLatency time.Duration 3793 remoteLeases int32 3794 sourceWeight float64 3795 sourceLeases int32 3796 meanLeases float64 3797 expected int32 3798 }{ 3799 // Evenly balanced leases stay balanced if requests are even 3800 {1, 0 * time.Millisecond, 10, 1, 10, 10, -2}, 3801 {1, 0 * time.Millisecond, 100, 1, 100, 100, -21}, 3802 {1, 0 * time.Millisecond, 1000, 1, 1000, 1000, -200}, 3803 {1, 10 * time.Millisecond, 10, 1, 10, 10, -2}, 3804 {1, 10 * time.Millisecond, 100, 1, 100, 100, -21}, 3805 {1, 10 * time.Millisecond, 1000, 1, 1000, 1000, -200}, 3806 {1, 50 * time.Millisecond, 10, 1, 10, 10, -2}, 3807 {1, 50 * time.Millisecond, 100, 1, 100, 100, -21}, 3808 {1, 50 * time.Millisecond, 1000, 1, 1000, 1000, -200}, 3809 {1000, 0 * time.Millisecond, 10, 1000, 10, 10, -2}, 3810 {1000, 0 * time.Millisecond, 100, 1000, 100, 100, -21}, 3811 {1000, 0 * time.Millisecond, 1000, 1000, 1000, 1000, -200}, 3812 {1000, 10 * time.Millisecond, 10, 1000, 10, 10, -2}, 3813 {1000, 10 * time.Millisecond, 100, 1000, 100, 100, -21}, 3814 {1000, 10 * time.Millisecond, 1000, 1000, 1000, 1000, -200}, 3815 {1000, 50 * time.Millisecond, 10, 1000, 10, 10, -2}, 3816 {1000, 50 * time.Millisecond, 100, 1000, 100, 100, -21}, 3817 {1000, 50 * time.Millisecond, 1000, 1000, 1000, 1000, -200}, 3818 // No latency favors lease balance despite request imbalance 3819 {10, 0 * time.Millisecond, 100, 1, 100, 100, -21}, 3820 {100, 0 * time.Millisecond, 100, 1, 100, 100, -21}, 3821 {1000, 0 * time.Millisecond, 100, 1, 100, 100, -21}, 3822 {10000, 0 * time.Millisecond, 100, 1, 100, 100, -21}, 3823 // Adding some latency changes that (perhaps a bit too much?) 3824 {10, 1 * time.Millisecond, 100, 1, 100, 100, -8}, 3825 {100, 1 * time.Millisecond, 100, 1, 100, 100, 6}, 3826 {1000, 1 * time.Millisecond, 100, 1, 100, 100, 20}, 3827 {10000, 1 * time.Millisecond, 100, 1, 100, 100, 34}, 3828 {10, 10 * time.Millisecond, 100, 1, 100, 100, 26}, 3829 {100, 10 * time.Millisecond, 100, 1, 100, 100, 74}, 3830 {1000, 10 * time.Millisecond, 100, 1, 100, 100, 122}, 3831 {10000, 10 * time.Millisecond, 100, 1, 100, 100, 170}, 3832 // Moving from very unbalanced to more balanced 3833 {1, 1 * time.Millisecond, 0, 1, 500, 200, 459}, 3834 {1, 1 * time.Millisecond, 0, 10, 500, 200, 432}, 3835 {1, 1 * time.Millisecond, 0, 100, 500, 200, 404}, 3836 {1, 10 * time.Millisecond, 0, 1, 500, 200, 459}, 3837 {1, 10 * time.Millisecond, 0, 10, 500, 200, 364}, 3838 {1, 10 * time.Millisecond, 0, 100, 500, 200, 268}, 3839 {1, 50 * time.Millisecond, 0, 1, 500, 200, 459}, 3840 {1, 50 * time.Millisecond, 0, 10, 500, 200, 302}, 3841 {1, 50 * time.Millisecond, 0, 100, 500, 200, 144}, 3842 {1, 1 * time.Millisecond, 50, 1, 500, 250, 400}, 3843 {1, 1 * time.Millisecond, 50, 10, 500, 250, 364}, 3844 {1, 1 * time.Millisecond, 50, 100, 500, 250, 330}, 3845 {1, 10 * time.Millisecond, 50, 1, 500, 250, 400}, 3846 {1, 10 * time.Millisecond, 50, 10, 500, 250, 280}, 3847 {1, 10 * time.Millisecond, 50, 100, 500, 250, 160}, 3848 {1, 50 * time.Millisecond, 50, 1, 500, 250, 400}, 3849 {1, 50 * time.Millisecond, 50, 10, 500, 250, 202}, 3850 {1, 50 * time.Millisecond, 50, 100, 500, 250, 6}, 3851 // Miscellaneous cases with uneven balance 3852 {10, 1 * time.Millisecond, 100, 1, 50, 67, -56}, 3853 {1, 1 * time.Millisecond, 50, 10, 100, 67, 26}, 3854 {10, 10 * time.Millisecond, 100, 1, 50, 67, -32}, 3855 {1, 10 * time.Millisecond, 50, 10, 100, 67, 4}, 3856 {10, 1 * time.Millisecond, 100, 1, 50, 80, -56}, 3857 {1, 1 * time.Millisecond, 50, 10, 100, 80, 22}, 3858 {10, 10 * time.Millisecond, 100, 1, 50, 80, -28}, 3859 {1, 10 * time.Millisecond, 50, 10, 100, 80, -6}, 3860 } 3861 3862 for _, c := range testCases { 3863 remoteStore.Capacity.LeaseCount = c.remoteLeases 3864 sourceStore.Capacity.LeaseCount = c.sourceLeases 3865 score, _ := loadBasedLeaseRebalanceScore( 3866 context.Background(), 3867 st, 3868 c.remoteWeight, 3869 c.remoteLatency, 3870 remoteStore, 3871 c.sourceWeight, 3872 sourceStore, 3873 c.meanLeases, 3874 ) 3875 if c.expected != score { 3876 t.Errorf("%+v: expected %d, got %d", c, c.expected, score) 3877 } 3878 } 3879 } 3880 3881 // TestAllocatorRemoveTarget verifies that the replica chosen by RemoveTarget is 3882 // the one with the lowest capacity. 3883 func TestAllocatorRemoveTarget(t *testing.T) { 3884 defer leaktest.AfterTest(t)() 3885 3886 // List of replicas that will be passed to RemoveTarget 3887 replicas := []roachpb.ReplicaDescriptor{ 3888 { 3889 StoreID: 1, 3890 NodeID: 1, 3891 ReplicaID: 1, 3892 }, 3893 { 3894 StoreID: 2, 3895 NodeID: 2, 3896 ReplicaID: 2, 3897 }, 3898 { 3899 StoreID: 3, 3900 NodeID: 3, 3901 ReplicaID: 3, 3902 }, 3903 { 3904 StoreID: 4, 3905 NodeID: 4, 3906 ReplicaID: 4, 3907 }, 3908 { 3909 StoreID: 5, 3910 NodeID: 5, 3911 ReplicaID: 5, 3912 }, 3913 } 3914 3915 // Setup the stores so that store 3 is the worst candidate and store 2 is 3916 // the 2nd worst. 3917 stores := []*roachpb.StoreDescriptor{ 3918 { 3919 StoreID: 1, 3920 Node: roachpb.NodeDescriptor{NodeID: 1}, 3921 Capacity: roachpb.StoreCapacity{Capacity: 100, Available: 100, RangeCount: 10}, 3922 }, 3923 { 3924 StoreID: 2, 3925 Node: roachpb.NodeDescriptor{NodeID: 2}, 3926 Capacity: roachpb.StoreCapacity{Capacity: 100, Available: 65, RangeCount: 14}, 3927 }, 3928 { 3929 StoreID: 3, 3930 Node: roachpb.NodeDescriptor{NodeID: 3}, 3931 Capacity: roachpb.StoreCapacity{Capacity: 100, Available: 60, RangeCount: 15}, 3932 }, 3933 { 3934 StoreID: 4, 3935 Node: roachpb.NodeDescriptor{NodeID: 4}, 3936 Capacity: roachpb.StoreCapacity{Capacity: 100, Available: 65, RangeCount: 10}, 3937 }, 3938 { 3939 StoreID: 5, 3940 Node: roachpb.NodeDescriptor{NodeID: 5}, 3941 Capacity: roachpb.StoreCapacity{Capacity: 100, Available: 65, RangeCount: 13}, 3942 }, 3943 } 3944 3945 ctx := context.Background() 3946 stopper, g, _, a, _ := createTestAllocator(10, false /* deterministic */) 3947 defer stopper.Stop(ctx) 3948 sg := gossiputil.NewStoreGossiper(g) 3949 sg.GossipStores(stores, t) 3950 3951 // Repeat this test 10 times, it should always be either store 2 or 3. 3952 for i := 0; i < 10; i++ { 3953 targetRepl, _, err := a.RemoveTarget( 3954 ctx, 3955 zonepb.EmptyCompleteZoneConfig(), 3956 replicas, 3957 replicas, 3958 ) 3959 if err != nil { 3960 t.Fatal(err) 3961 } 3962 if a, e1, e2 := targetRepl, replicas[1], replicas[2]; a != e1 && a != e2 { 3963 t.Fatalf("%d: RemoveTarget did not select either expected replica; expected %v or %v, got %v", 3964 i, e1, e2, a) 3965 } 3966 } 3967 } 3968 3969 func TestAllocatorComputeAction(t *testing.T) { 3970 defer leaktest.AfterTest(t)() 3971 3972 // Each test case should describe a repair situation which has a lower 3973 // priority than the previous test case. 3974 testCases := []struct { 3975 zone zonepb.ZoneConfig 3976 desc roachpb.RangeDescriptor 3977 expectedAction AllocatorAction 3978 }{ 3979 // Need three replicas, have three, one is on a dead store. 3980 { 3981 zone: zonepb.ZoneConfig{ 3982 NumReplicas: proto.Int32(3), 3983 Constraints: []zonepb.ConstraintsConjunction{{Constraints: []zonepb.Constraint{{Value: "us-east", Type: zonepb.Constraint_DEPRECATED_POSITIVE}}}}, 3984 RangeMinBytes: proto.Int64(0), 3985 RangeMaxBytes: proto.Int64(64000), 3986 }, 3987 desc: roachpb.RangeDescriptor{ 3988 InternalReplicas: []roachpb.ReplicaDescriptor{ 3989 { 3990 StoreID: 1, 3991 NodeID: 1, 3992 ReplicaID: 1, 3993 }, 3994 { 3995 StoreID: 2, 3996 NodeID: 2, 3997 ReplicaID: 2, 3998 }, 3999 { 4000 StoreID: 6, 4001 NodeID: 6, 4002 ReplicaID: 6, 4003 }, 4004 }, 4005 }, 4006 expectedAction: AllocatorReplaceDead, 4007 }, 4008 // Need five replicas, one is on a dead store. 4009 { 4010 zone: zonepb.ZoneConfig{ 4011 NumReplicas: proto.Int32(5), 4012 Constraints: []zonepb.ConstraintsConjunction{{Constraints: []zonepb.Constraint{{Value: "us-east", Type: zonepb.Constraint_DEPRECATED_POSITIVE}}}}, 4013 RangeMinBytes: proto.Int64(0), 4014 RangeMaxBytes: proto.Int64(64000), 4015 }, 4016 desc: roachpb.RangeDescriptor{ 4017 InternalReplicas: []roachpb.ReplicaDescriptor{ 4018 { 4019 StoreID: 1, 4020 NodeID: 1, 4021 ReplicaID: 1, 4022 }, 4023 { 4024 StoreID: 2, 4025 NodeID: 2, 4026 ReplicaID: 2, 4027 }, 4028 { 4029 StoreID: 3, 4030 NodeID: 3, 4031 ReplicaID: 3, 4032 }, 4033 { 4034 StoreID: 4, 4035 NodeID: 4, 4036 ReplicaID: 4, 4037 }, 4038 { 4039 StoreID: 6, 4040 NodeID: 6, 4041 ReplicaID: 6, 4042 }, 4043 }, 4044 }, 4045 expectedAction: AllocatorReplaceDead, 4046 }, 4047 // Need three replicas, have two. 4048 { 4049 zone: zonepb.ZoneConfig{ 4050 NumReplicas: proto.Int32(3), 4051 Constraints: []zonepb.ConstraintsConjunction{{Constraints: []zonepb.Constraint{{Value: "us-east", Type: zonepb.Constraint_DEPRECATED_POSITIVE}}}}, 4052 RangeMinBytes: proto.Int64(0), 4053 RangeMaxBytes: proto.Int64(64000), 4054 }, 4055 desc: roachpb.RangeDescriptor{ 4056 InternalReplicas: []roachpb.ReplicaDescriptor{ 4057 { 4058 StoreID: 1, 4059 NodeID: 1, 4060 ReplicaID: 1, 4061 }, 4062 { 4063 StoreID: 2, 4064 NodeID: 2, 4065 ReplicaID: 2, 4066 }, 4067 }, 4068 }, 4069 expectedAction: AllocatorAdd, 4070 }, 4071 // Need five replicas, have four, one is on a dead store. 4072 { 4073 zone: zonepb.ZoneConfig{ 4074 NumReplicas: proto.Int32(5), 4075 Constraints: []zonepb.ConstraintsConjunction{{Constraints: []zonepb.Constraint{{Value: "us-east", Type: zonepb.Constraint_DEPRECATED_POSITIVE}}}}, 4076 RangeMinBytes: proto.Int64(0), 4077 RangeMaxBytes: proto.Int64(64000), 4078 }, 4079 desc: roachpb.RangeDescriptor{ 4080 InternalReplicas: []roachpb.ReplicaDescriptor{ 4081 { 4082 StoreID: 1, 4083 NodeID: 1, 4084 ReplicaID: 1, 4085 }, 4086 { 4087 StoreID: 2, 4088 NodeID: 2, 4089 ReplicaID: 2, 4090 }, 4091 { 4092 StoreID: 3, 4093 NodeID: 3, 4094 ReplicaID: 3, 4095 }, 4096 { 4097 StoreID: 6, 4098 NodeID: 6, 4099 ReplicaID: 6, 4100 }, 4101 }, 4102 }, 4103 expectedAction: AllocatorAdd, 4104 }, 4105 // Need five replicas, have four. 4106 { 4107 zone: zonepb.ZoneConfig{ 4108 NumReplicas: proto.Int32(5), 4109 Constraints: []zonepb.ConstraintsConjunction{{Constraints: []zonepb.Constraint{{Value: "us-east", Type: zonepb.Constraint_DEPRECATED_POSITIVE}}}}, 4110 RangeMinBytes: proto.Int64(0), 4111 RangeMaxBytes: proto.Int64(64000), 4112 }, 4113 desc: roachpb.RangeDescriptor{ 4114 InternalReplicas: []roachpb.ReplicaDescriptor{ 4115 { 4116 StoreID: 1, 4117 NodeID: 1, 4118 ReplicaID: 1, 4119 }, 4120 { 4121 StoreID: 2, 4122 NodeID: 2, 4123 ReplicaID: 2, 4124 }, 4125 { 4126 StoreID: 3, 4127 NodeID: 3, 4128 ReplicaID: 3, 4129 }, 4130 { 4131 StoreID: 4, 4132 NodeID: 4, 4133 ReplicaID: 4, 4134 }, 4135 }, 4136 }, 4137 expectedAction: AllocatorAdd, 4138 }, 4139 // Need three replicas, have four, one is on a dead store. 4140 { 4141 zone: zonepb.ZoneConfig{ 4142 NumReplicas: proto.Int32(3), 4143 Constraints: []zonepb.ConstraintsConjunction{{Constraints: []zonepb.Constraint{{Value: "us-east", Type: zonepb.Constraint_DEPRECATED_POSITIVE}}}}, 4144 RangeMinBytes: proto.Int64(0), 4145 RangeMaxBytes: proto.Int64(64000), 4146 }, 4147 desc: roachpb.RangeDescriptor{ 4148 InternalReplicas: []roachpb.ReplicaDescriptor{ 4149 { 4150 StoreID: 1, 4151 NodeID: 1, 4152 ReplicaID: 1, 4153 }, 4154 { 4155 StoreID: 2, 4156 NodeID: 2, 4157 ReplicaID: 2, 4158 }, 4159 { 4160 StoreID: 3, 4161 NodeID: 3, 4162 ReplicaID: 3, 4163 }, 4164 { 4165 StoreID: 6, 4166 NodeID: 6, 4167 ReplicaID: 6, 4168 }, 4169 }, 4170 }, 4171 expectedAction: AllocatorRemoveDead, 4172 }, 4173 // Need five replicas, have six, one is on a dead store. 4174 { 4175 zone: zonepb.ZoneConfig{ 4176 NumReplicas: proto.Int32(5), 4177 Constraints: []zonepb.ConstraintsConjunction{{Constraints: []zonepb.Constraint{{Value: "us-east", Type: zonepb.Constraint_DEPRECATED_POSITIVE}}}}, 4178 RangeMinBytes: proto.Int64(0), 4179 RangeMaxBytes: proto.Int64(64000), 4180 }, 4181 desc: roachpb.RangeDescriptor{ 4182 InternalReplicas: []roachpb.ReplicaDescriptor{ 4183 { 4184 StoreID: 1, 4185 NodeID: 1, 4186 ReplicaID: 1, 4187 }, 4188 { 4189 StoreID: 2, 4190 NodeID: 2, 4191 ReplicaID: 2, 4192 }, 4193 { 4194 StoreID: 3, 4195 NodeID: 3, 4196 ReplicaID: 3, 4197 }, 4198 { 4199 StoreID: 4, 4200 NodeID: 4, 4201 ReplicaID: 4, 4202 }, 4203 { 4204 StoreID: 5, 4205 NodeID: 5, 4206 ReplicaID: 5, 4207 }, 4208 { 4209 StoreID: 6, 4210 NodeID: 6, 4211 ReplicaID: 6, 4212 }, 4213 }, 4214 }, 4215 expectedAction: AllocatorRemoveDead, 4216 }, 4217 // Need three replicas, have five, one is on a dead store. 4218 { 4219 zone: zonepb.ZoneConfig{ 4220 NumReplicas: proto.Int32(3), 4221 Constraints: []zonepb.ConstraintsConjunction{{Constraints: []zonepb.Constraint{{Value: "us-east", Type: zonepb.Constraint_DEPRECATED_POSITIVE}}}}, 4222 RangeMinBytes: proto.Int64(0), 4223 RangeMaxBytes: proto.Int64(64000), 4224 }, 4225 desc: roachpb.RangeDescriptor{ 4226 InternalReplicas: []roachpb.ReplicaDescriptor{ 4227 { 4228 StoreID: 1, 4229 NodeID: 1, 4230 ReplicaID: 1, 4231 }, 4232 { 4233 StoreID: 2, 4234 NodeID: 2, 4235 ReplicaID: 2, 4236 }, 4237 { 4238 StoreID: 3, 4239 NodeID: 3, 4240 ReplicaID: 3, 4241 }, 4242 { 4243 StoreID: 4, 4244 NodeID: 4, 4245 ReplicaID: 4, 4246 }, 4247 { 4248 StoreID: 6, 4249 NodeID: 6, 4250 ReplicaID: 6, 4251 }, 4252 }, 4253 }, 4254 expectedAction: AllocatorRemoveDead, 4255 }, 4256 // Need three replicas, have four. 4257 { 4258 zone: zonepb.ZoneConfig{ 4259 NumReplicas: proto.Int32(3), 4260 Constraints: []zonepb.ConstraintsConjunction{{Constraints: []zonepb.Constraint{{Value: "us-east", Type: zonepb.Constraint_DEPRECATED_POSITIVE}}}}, 4261 RangeMinBytes: proto.Int64(0), 4262 RangeMaxBytes: proto.Int64(64000), 4263 }, 4264 desc: roachpb.RangeDescriptor{ 4265 InternalReplicas: []roachpb.ReplicaDescriptor{ 4266 { 4267 StoreID: 1, 4268 NodeID: 1, 4269 ReplicaID: 1, 4270 }, 4271 { 4272 StoreID: 2, 4273 NodeID: 2, 4274 ReplicaID: 2, 4275 }, 4276 { 4277 StoreID: 3, 4278 NodeID: 3, 4279 ReplicaID: 3, 4280 }, 4281 { 4282 StoreID: 4, 4283 NodeID: 4, 4284 ReplicaID: 4, 4285 }, 4286 }, 4287 }, 4288 expectedAction: AllocatorRemove, 4289 }, 4290 // Need three replicas, have five. 4291 { 4292 zone: zonepb.ZoneConfig{ 4293 NumReplicas: proto.Int32(3), 4294 Constraints: []zonepb.ConstraintsConjunction{{Constraints: []zonepb.Constraint{{Value: "us-east", Type: zonepb.Constraint_DEPRECATED_POSITIVE}}}}, 4295 RangeMinBytes: proto.Int64(0), 4296 RangeMaxBytes: proto.Int64(64000), 4297 }, 4298 desc: roachpb.RangeDescriptor{ 4299 InternalReplicas: []roachpb.ReplicaDescriptor{ 4300 { 4301 StoreID: 1, 4302 NodeID: 1, 4303 ReplicaID: 1, 4304 }, 4305 { 4306 StoreID: 2, 4307 NodeID: 2, 4308 ReplicaID: 2, 4309 }, 4310 { 4311 StoreID: 3, 4312 NodeID: 3, 4313 ReplicaID: 3, 4314 }, 4315 { 4316 StoreID: 4, 4317 NodeID: 4, 4318 ReplicaID: 4, 4319 }, 4320 { 4321 StoreID: 5, 4322 NodeID: 5, 4323 ReplicaID: 5, 4324 }, 4325 }, 4326 }, 4327 expectedAction: AllocatorRemove, 4328 }, 4329 // Need three replicas, two are on dead stores. Should 4330 // be a noop because there aren't enough live replicas for 4331 // a quorum. 4332 { 4333 zone: zonepb.ZoneConfig{ 4334 NumReplicas: proto.Int32(3), 4335 Constraints: []zonepb.ConstraintsConjunction{{Constraints: []zonepb.Constraint{{Value: "us-east", Type: zonepb.Constraint_DEPRECATED_POSITIVE}}}}, 4336 RangeMinBytes: proto.Int64(0), 4337 RangeMaxBytes: proto.Int64(64000), 4338 }, 4339 desc: roachpb.RangeDescriptor{ 4340 InternalReplicas: []roachpb.ReplicaDescriptor{ 4341 { 4342 StoreID: 1, 4343 NodeID: 1, 4344 ReplicaID: 1, 4345 }, 4346 { 4347 StoreID: 7, 4348 NodeID: 7, 4349 ReplicaID: 7, 4350 }, 4351 { 4352 StoreID: 6, 4353 NodeID: 6, 4354 ReplicaID: 6, 4355 }, 4356 }, 4357 }, 4358 expectedAction: AllocatorRangeUnavailable, 4359 }, 4360 // Need three replicas, have three, none of the replicas in the store pool. 4361 { 4362 zone: zonepb.ZoneConfig{ 4363 NumReplicas: proto.Int32(3), 4364 Constraints: []zonepb.ConstraintsConjunction{{Constraints: []zonepb.Constraint{{Value: "us-east", Type: zonepb.Constraint_DEPRECATED_POSITIVE}}}}, 4365 RangeMinBytes: proto.Int64(0), 4366 RangeMaxBytes: proto.Int64(64000), 4367 }, 4368 desc: roachpb.RangeDescriptor{ 4369 InternalReplicas: []roachpb.ReplicaDescriptor{ 4370 { 4371 StoreID: 10, 4372 NodeID: 10, 4373 ReplicaID: 10, 4374 }, 4375 { 4376 StoreID: 20, 4377 NodeID: 20, 4378 ReplicaID: 20, 4379 }, 4380 { 4381 StoreID: 30, 4382 NodeID: 30, 4383 ReplicaID: 30, 4384 }, 4385 }, 4386 }, 4387 expectedAction: AllocatorRangeUnavailable, 4388 }, 4389 // Need three replicas, have three. 4390 { 4391 zone: zonepb.ZoneConfig{ 4392 NumReplicas: proto.Int32(3), 4393 Constraints: []zonepb.ConstraintsConjunction{{Constraints: []zonepb.Constraint{{Value: "us-east", Type: zonepb.Constraint_DEPRECATED_POSITIVE}}}}, 4394 RangeMinBytes: proto.Int64(0), 4395 RangeMaxBytes: proto.Int64(64000), 4396 }, 4397 desc: roachpb.RangeDescriptor{ 4398 InternalReplicas: []roachpb.ReplicaDescriptor{ 4399 { 4400 StoreID: 1, 4401 NodeID: 1, 4402 ReplicaID: 1, 4403 }, 4404 { 4405 StoreID: 2, 4406 NodeID: 2, 4407 ReplicaID: 2, 4408 }, 4409 { 4410 StoreID: 3, 4411 NodeID: 3, 4412 ReplicaID: 3, 4413 }, 4414 }, 4415 }, 4416 expectedAction: AllocatorConsiderRebalance, 4417 }, 4418 } 4419 4420 stopper, _, sp, a, _ := createTestAllocator(10, false /* deterministic */) 4421 ctx := context.Background() 4422 defer stopper.Stop(ctx) 4423 4424 // Set up eight stores. Stores six and seven are marked as dead. Replica eight 4425 // is dead. 4426 mockStorePool(sp, 4427 []roachpb.StoreID{1, 2, 3, 4, 5, 8}, 4428 nil, 4429 []roachpb.StoreID{6, 7}, 4430 nil, 4431 nil, 4432 ) 4433 4434 lastPriority := float64(999999999) 4435 for i, tcase := range testCases { 4436 action, priority := a.ComputeAction(ctx, &tcase.zone, &tcase.desc) 4437 if tcase.expectedAction != action { 4438 t.Errorf("Test case %d expected action %q, got action %q", 4439 i, allocatorActionNames[tcase.expectedAction], allocatorActionNames[action]) 4440 continue 4441 } 4442 if tcase.expectedAction != AllocatorNoop && priority > lastPriority { 4443 t.Errorf("Test cases should have descending priority. Case %d had priority %f, previous case had priority %f", i, priority, lastPriority) 4444 } 4445 lastPriority = priority 4446 } 4447 } 4448 4449 func TestAllocatorComputeActionRemoveDead(t *testing.T) { 4450 defer leaktest.AfterTest(t)() 4451 4452 zone := zonepb.ZoneConfig{ 4453 NumReplicas: proto.Int32(3), 4454 } 4455 threeReplDesc := roachpb.RangeDescriptor{ 4456 InternalReplicas: []roachpb.ReplicaDescriptor{ 4457 { 4458 StoreID: 1, 4459 NodeID: 1, 4460 ReplicaID: 1, 4461 }, 4462 { 4463 StoreID: 2, 4464 NodeID: 2, 4465 ReplicaID: 2, 4466 }, 4467 { 4468 StoreID: 3, 4469 NodeID: 3, 4470 ReplicaID: 3, 4471 }, 4472 }, 4473 } 4474 fourReplDesc := threeReplDesc 4475 fourReplDesc.InternalReplicas = append(fourReplDesc.InternalReplicas, roachpb.ReplicaDescriptor{ 4476 StoreID: 4, 4477 NodeID: 4, 4478 ReplicaID: 4, 4479 }) 4480 4481 // Each test case should describe a repair situation which has a lower 4482 // priority than the previous test case. 4483 testCases := []struct { 4484 desc roachpb.RangeDescriptor 4485 live []roachpb.StoreID 4486 dead []roachpb.StoreID 4487 expectedAction AllocatorAction 4488 }{ 4489 // Needs three replicas, one is dead, and there's no replacement. Since 4490 // there's no replacement we can't do anything, but an action is still 4491 // emitted. 4492 { 4493 desc: threeReplDesc, 4494 live: []roachpb.StoreID{1, 2}, 4495 dead: []roachpb.StoreID{3}, 4496 expectedAction: AllocatorReplaceDead, 4497 }, 4498 // Needs three replicas, one is dead, but there is a replacement. 4499 { 4500 desc: threeReplDesc, 4501 live: []roachpb.StoreID{1, 2, 4}, 4502 dead: []roachpb.StoreID{3}, 4503 expectedAction: AllocatorReplaceDead, 4504 }, 4505 // Needs three replicas, two are dead (i.e. the range lacks a quorum). 4506 { 4507 desc: threeReplDesc, 4508 live: []roachpb.StoreID{1, 4}, 4509 dead: []roachpb.StoreID{2, 3}, 4510 expectedAction: AllocatorRangeUnavailable, 4511 }, 4512 // Needs three replicas, has four, one is dead. 4513 { 4514 desc: fourReplDesc, 4515 live: []roachpb.StoreID{1, 2, 4}, 4516 dead: []roachpb.StoreID{3}, 4517 expectedAction: AllocatorRemoveDead, 4518 }, 4519 // Needs three replicas, has four, two are dead (i.e. the range lacks a quorum). 4520 { 4521 desc: fourReplDesc, 4522 live: []roachpb.StoreID{1, 4}, 4523 dead: []roachpb.StoreID{2, 3}, 4524 expectedAction: AllocatorRangeUnavailable, 4525 }, 4526 } 4527 4528 stopper, _, sp, a, _ := createTestAllocator(10, false /* deterministic */) 4529 ctx := context.Background() 4530 defer stopper.Stop(ctx) 4531 4532 for i, tcase := range testCases { 4533 mockStorePool(sp, tcase.live, nil, tcase.dead, nil, nil) 4534 action, _ := a.ComputeAction(ctx, &zone, &tcase.desc) 4535 if tcase.expectedAction != action { 4536 t.Errorf("Test case %d expected action %d, got action %d", i, tcase.expectedAction, action) 4537 } 4538 } 4539 } 4540 4541 func TestAllocatorComputeActionDecommission(t *testing.T) { 4542 defer leaktest.AfterTest(t)() 4543 4544 testCases := []struct { 4545 zone zonepb.ZoneConfig 4546 desc roachpb.RangeDescriptor 4547 expectedAction AllocatorAction 4548 live []roachpb.StoreID 4549 dead []roachpb.StoreID 4550 decommissioning []roachpb.StoreID 4551 decommissioned []roachpb.StoreID 4552 }{ 4553 // Has three replicas, but one is in decommissioning status. We can't 4554 // replace it (nor add a new replica) since there isn't a live target, 4555 // but that's still the action being emitted. 4556 { 4557 zone: zonepb.ZoneConfig{ 4558 NumReplicas: proto.Int32(3), 4559 }, 4560 desc: roachpb.RangeDescriptor{ 4561 InternalReplicas: []roachpb.ReplicaDescriptor{ 4562 { 4563 StoreID: 1, 4564 NodeID: 1, 4565 ReplicaID: 1, 4566 }, 4567 { 4568 StoreID: 2, 4569 NodeID: 2, 4570 ReplicaID: 2, 4571 }, 4572 { 4573 StoreID: 3, 4574 NodeID: 3, 4575 ReplicaID: 3, 4576 }, 4577 }, 4578 }, 4579 expectedAction: AllocatorReplaceDecommissioning, 4580 live: []roachpb.StoreID{1, 2}, 4581 dead: nil, 4582 decommissioning: []roachpb.StoreID{3}, 4583 }, 4584 // Has three replicas, one is in decommissioning status, and one is on a 4585 // dead node. Replacing the dead replica is more important. 4586 { 4587 zone: zonepb.ZoneConfig{ 4588 NumReplicas: proto.Int32(3), 4589 }, 4590 desc: roachpb.RangeDescriptor{ 4591 InternalReplicas: []roachpb.ReplicaDescriptor{ 4592 { 4593 StoreID: 1, 4594 NodeID: 1, 4595 ReplicaID: 1, 4596 }, 4597 { 4598 StoreID: 2, 4599 NodeID: 2, 4600 ReplicaID: 2, 4601 }, 4602 { 4603 StoreID: 3, 4604 NodeID: 3, 4605 ReplicaID: 3, 4606 }, 4607 }, 4608 }, 4609 expectedAction: AllocatorReplaceDead, 4610 live: []roachpb.StoreID{1}, 4611 dead: []roachpb.StoreID{2}, 4612 decommissioning: []roachpb.StoreID{3}, 4613 }, 4614 // Needs three replicas, has four, where one is decommissioning and one is 4615 // dead. 4616 { 4617 zone: zonepb.ZoneConfig{ 4618 NumReplicas: proto.Int32(3), 4619 }, 4620 desc: roachpb.RangeDescriptor{ 4621 InternalReplicas: []roachpb.ReplicaDescriptor{ 4622 { 4623 StoreID: 1, 4624 NodeID: 1, 4625 ReplicaID: 1, 4626 }, 4627 { 4628 StoreID: 2, 4629 NodeID: 2, 4630 ReplicaID: 2, 4631 }, 4632 { 4633 StoreID: 3, 4634 NodeID: 3, 4635 ReplicaID: 3, 4636 }, 4637 { 4638 StoreID: 4, 4639 NodeID: 4, 4640 ReplicaID: 4, 4641 }, 4642 }, 4643 }, 4644 expectedAction: AllocatorRemoveDead, 4645 live: []roachpb.StoreID{1, 4}, 4646 dead: []roachpb.StoreID{2}, 4647 decommissioning: []roachpb.StoreID{3}, 4648 }, 4649 // Needs three replicas, has four, where one is decommissioning and one is 4650 // decommissioned. 4651 { 4652 zone: zonepb.ZoneConfig{ 4653 NumReplicas: proto.Int32(3), 4654 }, 4655 desc: roachpb.RangeDescriptor{ 4656 InternalReplicas: []roachpb.ReplicaDescriptor{ 4657 { 4658 StoreID: 1, 4659 NodeID: 1, 4660 ReplicaID: 1, 4661 }, 4662 { 4663 StoreID: 2, 4664 NodeID: 2, 4665 ReplicaID: 2, 4666 }, 4667 { 4668 StoreID: 3, 4669 NodeID: 3, 4670 ReplicaID: 3, 4671 }, 4672 { 4673 StoreID: 4, 4674 NodeID: 4, 4675 ReplicaID: 4, 4676 }, 4677 }, 4678 }, 4679 expectedAction: AllocatorRemoveDead, 4680 live: []roachpb.StoreID{1, 4}, 4681 dead: nil, 4682 decommissioning: []roachpb.StoreID{3}, 4683 decommissioned: []roachpb.StoreID{2}, 4684 }, 4685 // Needs three replicas, has three, all decommissioning 4686 { 4687 zone: zonepb.ZoneConfig{ 4688 NumReplicas: proto.Int32(3), 4689 }, 4690 desc: roachpb.RangeDescriptor{ 4691 InternalReplicas: []roachpb.ReplicaDescriptor{ 4692 { 4693 StoreID: 1, 4694 NodeID: 1, 4695 ReplicaID: 1, 4696 }, 4697 { 4698 StoreID: 2, 4699 NodeID: 2, 4700 ReplicaID: 2, 4701 }, 4702 { 4703 StoreID: 3, 4704 NodeID: 3, 4705 ReplicaID: 3, 4706 }, 4707 }, 4708 }, 4709 expectedAction: AllocatorReplaceDecommissioning, 4710 live: nil, 4711 dead: nil, 4712 decommissioning: []roachpb.StoreID{1, 2, 3}, 4713 }, 4714 // Needs 3. Has 1 live, 3 decommissioning. 4715 { 4716 zone: zonepb.ZoneConfig{ 4717 NumReplicas: proto.Int32(3), 4718 }, 4719 desc: roachpb.RangeDescriptor{ 4720 InternalReplicas: []roachpb.ReplicaDescriptor{ 4721 { 4722 StoreID: 1, 4723 NodeID: 1, 4724 ReplicaID: 1, 4725 }, 4726 { 4727 StoreID: 2, 4728 NodeID: 2, 4729 ReplicaID: 2, 4730 }, 4731 { 4732 StoreID: 3, 4733 NodeID: 3, 4734 ReplicaID: 3, 4735 }, 4736 { 4737 StoreID: 4, 4738 NodeID: 4, 4739 ReplicaID: 4, 4740 }, 4741 }, 4742 }, 4743 expectedAction: AllocatorRemoveDecommissioning, 4744 live: []roachpb.StoreID{4}, 4745 dead: nil, 4746 decommissioning: []roachpb.StoreID{1, 2, 3}, 4747 }, 4748 } 4749 4750 stopper, _, sp, a, _ := createTestAllocator(10, false /* deterministic */) 4751 ctx := context.Background() 4752 defer stopper.Stop(ctx) 4753 4754 for i, tcase := range testCases { 4755 mockStorePool(sp, tcase.live, nil, tcase.dead, tcase.decommissioning, tcase.decommissioned) 4756 action, _ := a.ComputeAction(ctx, &tcase.zone, &tcase.desc) 4757 if tcase.expectedAction != action { 4758 t.Errorf("Test case %d expected action %s, got action %s", i, tcase.expectedAction, action) 4759 continue 4760 } 4761 } 4762 } 4763 4764 func TestAllocatorRemoveLearner(t *testing.T) { 4765 defer leaktest.AfterTest(t)() 4766 4767 zone := zonepb.ZoneConfig{ 4768 NumReplicas: proto.Int32(3), 4769 } 4770 learnerType := roachpb.LEARNER 4771 rangeWithLearnerDesc := roachpb.RangeDescriptor{ 4772 InternalReplicas: []roachpb.ReplicaDescriptor{ 4773 { 4774 StoreID: 1, 4775 NodeID: 1, 4776 ReplicaID: 1, 4777 }, 4778 { 4779 StoreID: 2, 4780 NodeID: 2, 4781 ReplicaID: 2, 4782 Type: &learnerType, 4783 }, 4784 }, 4785 } 4786 4787 // Removing a learner is prioritized over adding a new replica to an under 4788 // replicated range. 4789 stopper, _, sp, a, _ := createTestAllocator(10, false /* deterministic */) 4790 ctx := context.Background() 4791 defer stopper.Stop(ctx) 4792 live, dead := []roachpb.StoreID{1, 2}, []roachpb.StoreID{3} 4793 mockStorePool(sp, live, nil, dead, nil, nil) 4794 action, _ := a.ComputeAction(ctx, &zone, &rangeWithLearnerDesc) 4795 require.Equal(t, AllocatorRemoveLearner, action) 4796 } 4797 4798 func TestAllocatorComputeActionDynamicNumReplicas(t *testing.T) { 4799 defer leaktest.AfterTest(t)() 4800 4801 // In this test, the configured zone config has a replication factor of five 4802 // set. We are checking that the effective replication factor is rounded down 4803 // to the number of stores which are not decommissioned or decommissioning. 4804 testCases := []struct { 4805 storeList []roachpb.StoreID 4806 expectedNumReplicas int 4807 expectedAction AllocatorAction 4808 live []roachpb.StoreID 4809 unavailable []roachpb.StoreID 4810 dead []roachpb.StoreID 4811 decommissioning []roachpb.StoreID 4812 }{ 4813 { 4814 // Four known stores, three of them are decommissioning, so effective 4815 // replication factor would be 1 if we hadn't decided that we'll never 4816 // drop past 3, so 3 it is. 4817 storeList: []roachpb.StoreID{1, 2, 3, 4}, 4818 expectedNumReplicas: 3, 4819 expectedAction: AllocatorRemoveDecommissioning, 4820 live: []roachpb.StoreID{4}, 4821 unavailable: nil, 4822 dead: nil, 4823 decommissioning: []roachpb.StoreID{1, 2, 3}, 4824 }, 4825 { 4826 // Ditto. 4827 storeList: []roachpb.StoreID{1, 2, 3}, 4828 expectedNumReplicas: 3, 4829 expectedAction: AllocatorReplaceDecommissioning, 4830 live: []roachpb.StoreID{4, 5}, 4831 unavailable: nil, 4832 dead: nil, 4833 decommissioning: []roachpb.StoreID{1, 2, 3}, 4834 }, 4835 { 4836 // Four live stores and one dead one, so the effective replication 4837 // factor would be even (four), in which case we drop down one more 4838 // to three. Then the right thing becomes removing the dead replica 4839 // from the range at hand, rather than trying to replace it. 4840 storeList: []roachpb.StoreID{1, 2, 3, 4}, 4841 expectedNumReplicas: 3, 4842 expectedAction: AllocatorRemoveDead, 4843 live: []roachpb.StoreID{1, 2, 3, 5}, 4844 unavailable: nil, 4845 dead: []roachpb.StoreID{4}, 4846 decommissioning: nil, 4847 }, 4848 { 4849 // Two replicas, one on a dead store, but we have four live nodes 4850 // in the system which amounts to an effective replication factor 4851 // of three (avoiding the even number). Adding a replica is more 4852 // important than replacing the dead one. 4853 storeList: []roachpb.StoreID{1, 4}, 4854 expectedNumReplicas: 3, 4855 expectedAction: AllocatorAdd, 4856 live: []roachpb.StoreID{1, 2, 3, 5}, 4857 unavailable: nil, 4858 dead: []roachpb.StoreID{4}, 4859 decommissioning: nil, 4860 }, 4861 { 4862 // Similar to above, but nothing to do. 4863 storeList: []roachpb.StoreID{1, 2, 3}, 4864 expectedNumReplicas: 3, 4865 expectedAction: AllocatorConsiderRebalance, 4866 live: []roachpb.StoreID{1, 2, 3, 4}, 4867 unavailable: nil, 4868 dead: nil, 4869 decommissioning: nil, 4870 }, 4871 { 4872 // Effective replication factor can't dip below three (unless the 4873 // zone config explicitly asks for that, which it does not), so three 4874 // it is and we are under-replicaed. 4875 storeList: []roachpb.StoreID{1, 2}, 4876 expectedNumReplicas: 3, 4877 expectedAction: AllocatorAdd, 4878 live: []roachpb.StoreID{1, 2}, 4879 unavailable: nil, 4880 dead: nil, 4881 decommissioning: nil, 4882 }, 4883 { 4884 // Three and happy. 4885 storeList: []roachpb.StoreID{1, 2, 3}, 4886 expectedNumReplicas: 3, 4887 expectedAction: AllocatorConsiderRebalance, 4888 live: []roachpb.StoreID{1, 2, 3}, 4889 unavailable: nil, 4890 dead: nil, 4891 decommissioning: nil, 4892 }, 4893 { 4894 // Three again, on account of avoiding the even four. 4895 storeList: []roachpb.StoreID{1, 2, 3, 4}, 4896 expectedNumReplicas: 3, 4897 expectedAction: AllocatorRemove, 4898 live: []roachpb.StoreID{1, 2, 3, 4}, 4899 unavailable: nil, 4900 dead: nil, 4901 decommissioning: nil, 4902 }, 4903 { 4904 // The usual case in which there are enough nodes to accommodate the 4905 // zone config. 4906 storeList: []roachpb.StoreID{1, 2, 3, 4, 5}, 4907 expectedNumReplicas: 5, 4908 expectedAction: AllocatorConsiderRebalance, 4909 live: []roachpb.StoreID{1, 2, 3, 4, 5}, 4910 unavailable: nil, 4911 dead: nil, 4912 decommissioning: nil, 4913 }, 4914 { 4915 // No dead or decommissioning node and enough nodes around, so 4916 // sticking with the zone config. 4917 storeList: []roachpb.StoreID{1, 2, 3, 4, 5}, 4918 expectedNumReplicas: 5, 4919 expectedAction: AllocatorConsiderRebalance, 4920 live: []roachpb.StoreID{1, 2, 3, 4}, 4921 unavailable: []roachpb.StoreID{5}, 4922 dead: nil, 4923 decommissioning: nil, 4924 }, 4925 { 4926 // Ditto. 4927 storeList: []roachpb.StoreID{1, 2, 3, 4, 5}, 4928 expectedNumReplicas: 5, 4929 expectedAction: AllocatorConsiderRebalance, 4930 live: []roachpb.StoreID{1, 2, 3}, 4931 unavailable: []roachpb.StoreID{4, 5}, 4932 dead: nil, 4933 decommissioning: nil, 4934 }, 4935 { 4936 // Ditto, but we've lost quorum. 4937 storeList: []roachpb.StoreID{1, 2, 3, 4, 5}, 4938 expectedNumReplicas: 5, 4939 expectedAction: AllocatorRangeUnavailable, 4940 live: []roachpb.StoreID{1, 2}, 4941 unavailable: []roachpb.StoreID{3, 4, 5}, 4942 dead: nil, 4943 decommissioning: nil, 4944 }, 4945 { 4946 // Ditto (dead nodes don't reduce NumReplicas, only decommissioning 4947 // or decommissioned do, and both correspond to the 'decommissioning' 4948 // slice in these tests). 4949 storeList: []roachpb.StoreID{1, 2, 3, 4, 5}, 4950 expectedNumReplicas: 5, 4951 expectedAction: AllocatorReplaceDead, 4952 live: []roachpb.StoreID{1, 2, 3}, 4953 unavailable: []roachpb.StoreID{4}, 4954 dead: []roachpb.StoreID{5}, 4955 decommissioning: nil, 4956 }, 4957 { 4958 // Avoiding four, so getting three, and since there is no dead store 4959 // the most important thing is removing a decommissioning replica. 4960 storeList: []roachpb.StoreID{1, 2, 3, 4, 5}, 4961 expectedNumReplicas: 3, 4962 expectedAction: AllocatorRemoveDecommissioning, 4963 live: []roachpb.StoreID{1, 2, 3}, 4964 unavailable: []roachpb.StoreID{4}, 4965 dead: nil, 4966 decommissioning: []roachpb.StoreID{5}, 4967 }, 4968 } 4969 4970 var numNodes int 4971 stopper, _, _, sp, _ := createTestStorePool( 4972 TestTimeUntilStoreDeadOff, false, /* deterministic */ 4973 func() int { return numNodes }, 4974 kvserverpb.NodeLivenessStatus_LIVE) 4975 a := MakeAllocator(sp, func(string) (time.Duration, bool) { 4976 return 0, true 4977 }) 4978 4979 ctx := context.Background() 4980 defer stopper.Stop(ctx) 4981 zone := &zonepb.ZoneConfig{ 4982 NumReplicas: proto.Int32(5), 4983 } 4984 4985 for _, prefixKey := range []roachpb.RKey{ 4986 roachpb.RKey(keys.NodeLivenessPrefix), 4987 roachpb.RKey(keys.SystemPrefix), 4988 } { 4989 for _, c := range testCases { 4990 t.Run(prefixKey.String(), func(t *testing.T) { 4991 numNodes = len(c.storeList) - len(c.decommissioning) 4992 mockStorePool(sp, c.live, c.unavailable, c.dead, 4993 c.decommissioning, []roachpb.StoreID{}) 4994 desc := makeDescriptor(c.storeList) 4995 desc.EndKey = prefixKey 4996 4997 clusterNodes := a.storePool.ClusterNodeCount() 4998 effectiveNumReplicas := GetNeededReplicas(*zone.NumReplicas, clusterNodes) 4999 require.Equal(t, c.expectedNumReplicas, effectiveNumReplicas, "clusterNodes=%d", clusterNodes) 5000 5001 action, _ := a.ComputeAction(ctx, zone, &desc) 5002 require.Equal(t, c.expectedAction.String(), action.String()) 5003 }) 5004 } 5005 } 5006 } 5007 5008 func TestAllocatorGetNeededReplicas(t *testing.T) { 5009 defer leaktest.AfterTest(t)() 5010 5011 testCases := []struct { 5012 zoneRepls int32 5013 availNodes int 5014 expected int 5015 }{ 5016 // If zone.NumReplicas <= 3, GetNeededReplicas should always return zone.NumReplicas. 5017 {1, 0, 1}, 5018 {1, 1, 1}, 5019 {2, 0, 2}, 5020 {2, 1, 2}, 5021 {2, 2, 2}, 5022 {3, 0, 3}, 5023 {3, 1, 3}, 5024 {3, 3, 3}, 5025 // Things get more involved when zone.NumReplicas > 3. 5026 {4, 1, 3}, 5027 {4, 2, 3}, 5028 {4, 3, 3}, 5029 {4, 4, 4}, 5030 {5, 1, 3}, 5031 {5, 2, 3}, 5032 {5, 3, 3}, 5033 {5, 4, 3}, 5034 {5, 5, 5}, 5035 {6, 1, 3}, 5036 {6, 2, 3}, 5037 {6, 3, 3}, 5038 {6, 4, 3}, 5039 {6, 5, 5}, 5040 {6, 6, 6}, 5041 {7, 1, 3}, 5042 {7, 2, 3}, 5043 {7, 3, 3}, 5044 {7, 4, 3}, 5045 {7, 5, 5}, 5046 {7, 6, 5}, 5047 {7, 7, 7}, 5048 } 5049 5050 for _, tc := range testCases { 5051 if e, a := tc.expected, GetNeededReplicas(tc.zoneRepls, tc.availNodes); e != a { 5052 t.Errorf( 5053 "GetNeededReplicas(zone.NumReplicas=%d, availNodes=%d) got %d; want %d", 5054 tc.zoneRepls, tc.availNodes, a, e) 5055 } 5056 } 5057 } 5058 5059 func makeDescriptor(storeList []roachpb.StoreID) roachpb.RangeDescriptor { 5060 desc := roachpb.RangeDescriptor{ 5061 EndKey: roachpb.RKey(keys.SystemPrefix), 5062 } 5063 5064 desc.InternalReplicas = make([]roachpb.ReplicaDescriptor, len(storeList)) 5065 5066 for i, node := range storeList { 5067 desc.InternalReplicas[i] = roachpb.ReplicaDescriptor{ 5068 StoreID: node, 5069 NodeID: roachpb.NodeID(node), 5070 ReplicaID: roachpb.ReplicaID(node), 5071 } 5072 } 5073 5074 return desc 5075 } 5076 5077 // TestAllocatorComputeActionNoStorePool verifies that 5078 // ComputeAction returns AllocatorNoop when storePool is nil. 5079 func TestAllocatorComputeActionNoStorePool(t *testing.T) { 5080 defer leaktest.AfterTest(t)() 5081 5082 a := MakeAllocator(nil /* storePool */, nil /* rpcContext */) 5083 action, priority := a.ComputeAction(context.Background(), &zonepb.ZoneConfig{NumReplicas: proto.Int32(0)}, nil) 5084 if action != AllocatorNoop { 5085 t.Errorf("expected AllocatorNoop, but got %v", action) 5086 } 5087 if priority != 0 { 5088 t.Errorf("expected priority 0, but got %f", priority) 5089 } 5090 } 5091 5092 // TestAllocatorError ensures that the correctly formatted error message is 5093 // returned from an allocatorError. 5094 func TestAllocatorError(t *testing.T) { 5095 defer leaktest.AfterTest(t)() 5096 5097 constraint := []zonepb.ConstraintsConjunction{ 5098 {Constraints: []zonepb.Constraint{{Value: "one", Type: zonepb.Constraint_REQUIRED}}}, 5099 } 5100 constraints := []zonepb.ConstraintsConjunction{ 5101 { 5102 Constraints: []zonepb.Constraint{ 5103 {Value: "one", Type: zonepb.Constraint_REQUIRED}, 5104 {Value: "two", Type: zonepb.Constraint_REQUIRED}, 5105 }, 5106 }, 5107 } 5108 5109 testCases := []struct { 5110 ae allocatorError 5111 expected string 5112 }{ 5113 {allocatorError{constraints: nil, existingReplicas: 1, aliveStores: 1}, 5114 "0 of 1 live stores are able to take a new replica for the range (1 already has a replica); likely not enough nodes in cluster"}, 5115 {allocatorError{constraints: nil, existingReplicas: 1, aliveStores: 2, throttledStores: 1}, 5116 "0 of 2 live stores are able to take a new replica for the range (1 throttled, 1 already has a replica)"}, 5117 {allocatorError{constraints: constraint, existingReplicas: 1, aliveStores: 1}, 5118 `0 of 1 live stores are able to take a new replica for the range (1 already has a replica); ` + 5119 `must match constraints [{+one}]`}, 5120 {allocatorError{constraints: constraint, existingReplicas: 1, aliveStores: 2}, 5121 `0 of 2 live stores are able to take a new replica for the range (1 already has a replica); ` + 5122 `must match constraints [{+one}]`}, 5123 {allocatorError{constraints: constraints, existingReplicas: 1, aliveStores: 1}, 5124 `0 of 1 live stores are able to take a new replica for the range (1 already has a replica); ` + 5125 `must match constraints [{+one,+two}]`}, 5126 {allocatorError{constraints: constraints, existingReplicas: 1, aliveStores: 2}, 5127 `0 of 2 live stores are able to take a new replica for the range (1 already has a replica); ` + 5128 `must match constraints [{+one,+two}]`}, 5129 {allocatorError{constraints: constraint, existingReplicas: 1, aliveStores: 2, throttledStores: 1}, 5130 `0 of 2 live stores are able to take a new replica for the range (1 throttled, 1 already has a replica); ` + 5131 `must match constraints [{+one}]`}, 5132 } 5133 5134 for i, testCase := range testCases { 5135 assert.EqualErrorf(t, &testCase.ae, testCase.expected, "test case: %d", i) 5136 } 5137 } 5138 5139 // TestAllocatorThrottled ensures that when a store is throttled, the replica 5140 // will not be sent to purgatory. 5141 func TestAllocatorThrottled(t *testing.T) { 5142 defer leaktest.AfterTest(t)() 5143 5144 stopper, g, _, a, _ := createTestAllocator(10, false /* deterministic */) 5145 ctx := context.Background() 5146 defer stopper.Stop(ctx) 5147 5148 // First test to make sure we would send the replica to purgatory. 5149 _, _, err := a.AllocateTarget( 5150 ctx, 5151 &simpleZoneConfig, 5152 []roachpb.ReplicaDescriptor{}, 5153 ) 5154 if !errors.HasInterface(err, (*purgatoryError)(nil)) { 5155 t.Fatalf("expected a purgatory error, got: %+v", err) 5156 } 5157 5158 // Second, test the normal case in which we can allocate to the store. 5159 gossiputil.NewStoreGossiper(g).GossipStores(singleStore, t) 5160 result, _, err := a.AllocateTarget( 5161 ctx, 5162 &simpleZoneConfig, 5163 []roachpb.ReplicaDescriptor{}, 5164 ) 5165 if err != nil { 5166 t.Fatalf("unable to perform allocation: %+v", err) 5167 } 5168 if result.Node.NodeID != 1 || result.StoreID != 1 { 5169 t.Errorf("expected NodeID 1 and StoreID 1: %+v", result) 5170 } 5171 5172 // Finally, set that store to be throttled and ensure we don't send the 5173 // replica to purgatory. 5174 a.storePool.detailsMu.Lock() 5175 storeDetail, ok := a.storePool.detailsMu.storeDetails[singleStore[0].StoreID] 5176 if !ok { 5177 t.Fatalf("store:%d was not found in the store pool", singleStore[0].StoreID) 5178 } 5179 storeDetail.throttledUntil = timeutil.Now().Add(24 * time.Hour) 5180 a.storePool.detailsMu.Unlock() 5181 _, _, err = a.AllocateTarget( 5182 ctx, 5183 &simpleZoneConfig, 5184 []roachpb.ReplicaDescriptor{}, 5185 ) 5186 if errors.HasInterface(err, (*purgatoryError)(nil)) { 5187 t.Fatalf("expected a non purgatory error, got: %+v", err) 5188 } 5189 } 5190 5191 func TestFilterBehindReplicas(t *testing.T) { 5192 defer leaktest.AfterTest(t)() 5193 ctx := context.Background() 5194 5195 testCases := []struct { 5196 commit uint64 5197 leader uint64 5198 progress []uint64 5199 expected []uint64 5200 }{ 5201 {0, 99, []uint64{0}, nil}, 5202 {1, 99, []uint64{1}, []uint64{1}}, 5203 {2, 99, []uint64{2}, []uint64{2}}, 5204 {1, 99, []uint64{0, 1}, []uint64{1}}, 5205 {1, 99, []uint64{1, 2}, []uint64{1, 2}}, 5206 {2, 99, []uint64{3, 2}, []uint64{3, 2}}, 5207 {1, 99, []uint64{0, 0, 1}, []uint64{1}}, 5208 {1, 99, []uint64{0, 1, 2}, []uint64{1, 2}}, 5209 {2, 99, []uint64{1, 2, 3}, []uint64{2, 3}}, 5210 {3, 99, []uint64{4, 3, 2}, []uint64{4, 3}}, 5211 {1, 99, []uint64{1, 1, 1}, []uint64{1, 1, 1}}, 5212 {1, 99, []uint64{1, 1, 2}, []uint64{1, 1, 2}}, 5213 {2, 99, []uint64{1, 2, 2}, []uint64{2, 2}}, 5214 {2, 99, []uint64{0, 1, 2, 3}, []uint64{2, 3}}, 5215 {2, 99, []uint64{1, 2, 3, 4}, []uint64{2, 3, 4}}, 5216 {3, 99, []uint64{5, 4, 3, 2}, []uint64{5, 4, 3}}, 5217 {3, 99, []uint64{1, 2, 3, 4, 5}, []uint64{3, 4, 5}}, 5218 {4, 99, []uint64{6, 5, 4, 3, 2}, []uint64{6, 5, 4}}, 5219 {4, 99, []uint64{6, 5, 4, 3, 2}, []uint64{6, 5, 4}}, 5220 {0, 1, []uint64{0}, []uint64{0}}, 5221 {0, 1, []uint64{0, 0, 0}, []uint64{0}}, 5222 {1, 1, []uint64{2, 0, 1}, []uint64{2, 1}}, 5223 {1, 2, []uint64{0, 2, 1}, []uint64{2, 1}}, 5224 } 5225 for _, c := range testCases { 5226 t.Run("", func(t *testing.T) { 5227 status := &raft.Status{ 5228 Progress: make(map[uint64]tracker.Progress), 5229 } 5230 status.Lead = c.leader 5231 status.Commit = c.commit 5232 var replicas []roachpb.ReplicaDescriptor 5233 for j, v := range c.progress { 5234 p := tracker.Progress{ 5235 Match: v, 5236 State: tracker.StateReplicate, 5237 } 5238 if v == 0 { 5239 p.State = tracker.StateProbe 5240 } 5241 replicaID := uint64(j + 1) 5242 status.Progress[replicaID] = p 5243 replicas = append(replicas, roachpb.ReplicaDescriptor{ 5244 ReplicaID: roachpb.ReplicaID(replicaID), 5245 StoreID: roachpb.StoreID(v), 5246 }) 5247 } 5248 candidates := filterBehindReplicas(ctx, status, replicas) 5249 var ids []uint64 5250 for _, c := range candidates { 5251 ids = append(ids, uint64(c.StoreID)) 5252 } 5253 if !reflect.DeepEqual(c.expected, ids) { 5254 t.Fatalf("expected %d, but got %d", c.expected, ids) 5255 } 5256 }) 5257 } 5258 } 5259 5260 func TestFilterUnremovableReplicas(t *testing.T) { 5261 defer leaktest.AfterTest(t)() 5262 ctx := context.Background() 5263 5264 testCases := []struct { 5265 commit uint64 5266 progress []uint64 5267 brandNewReplicaID roachpb.ReplicaID 5268 expected []uint64 5269 }{ 5270 {0, []uint64{0}, 0, nil}, 5271 {1, []uint64{1}, 0, nil}, 5272 {1, []uint64{0, 1}, 0, nil}, 5273 {1, []uint64{1, 2}, 0, []uint64{1, 2}}, 5274 {1, []uint64{1, 2, 3}, 0, []uint64{1, 2, 3}}, 5275 {2, []uint64{1, 2, 3}, 0, []uint64{1}}, 5276 {3, []uint64{1, 2, 3}, 0, nil}, 5277 {1, []uint64{1, 2, 3, 4}, 0, []uint64{1, 2, 3, 4}}, 5278 {2, []uint64{1, 2, 3, 4}, 0, []uint64{1, 2, 3, 4}}, 5279 {3, []uint64{1, 2, 3, 4}, 0, nil}, 5280 {2, []uint64{1, 2, 3, 4, 5}, 0, []uint64{1, 2, 3, 4, 5}}, 5281 {3, []uint64{1, 2, 3, 4, 5}, 0, []uint64{1, 2}}, 5282 {1, []uint64{1, 0}, 2, nil}, 5283 {1, []uint64{2, 1}, 2, []uint64{2}}, 5284 {1, []uint64{1, 0}, 1, nil}, 5285 {1, []uint64{2, 1}, 1, []uint64{1}}, 5286 {3, []uint64{3, 2, 1}, 3, nil}, 5287 {3, []uint64{3, 2, 0}, 3, nil}, 5288 {2, []uint64{4, 3, 2, 1}, 4, []uint64{4, 3, 2}}, 5289 {2, []uint64{4, 3, 2, 0}, 3, []uint64{4, 3, 0}}, 5290 {2, []uint64{4, 3, 2, 0}, 4, []uint64{4, 3, 2}}, 5291 {3, []uint64{4, 3, 2, 1}, 0, nil}, 5292 {3, []uint64{4, 3, 2, 1}, 4, nil}, 5293 } 5294 for _, c := range testCases { 5295 t.Run("", func(t *testing.T) { 5296 status := &raft.Status{ 5297 Progress: make(map[uint64]tracker.Progress), 5298 } 5299 // Use an invalid replica ID for the leader. TestFilterBehindReplicas covers 5300 // valid replica IDs. 5301 status.Lead = 99 5302 status.Commit = c.commit 5303 var replicas []roachpb.ReplicaDescriptor 5304 for j, v := range c.progress { 5305 p := tracker.Progress{ 5306 Match: v, 5307 State: tracker.StateReplicate, 5308 } 5309 if v == 0 { 5310 p.State = tracker.StateProbe 5311 } 5312 replicaID := uint64(j + 1) 5313 status.Progress[replicaID] = p 5314 replicas = append(replicas, roachpb.ReplicaDescriptor{ 5315 ReplicaID: roachpb.ReplicaID(replicaID), 5316 StoreID: roachpb.StoreID(v), 5317 }) 5318 } 5319 5320 candidates := filterUnremovableReplicas(ctx, status, replicas, c.brandNewReplicaID) 5321 var ids []uint64 5322 for _, c := range candidates { 5323 ids = append(ids, uint64(c.StoreID)) 5324 } 5325 if !reflect.DeepEqual(c.expected, ids) { 5326 t.Fatalf("expected %d, but got %d", c.expected, ids) 5327 } 5328 }) 5329 } 5330 } 5331 5332 func TestSimulateFilterUnremovableReplicas(t *testing.T) { 5333 defer leaktest.AfterTest(t)() 5334 ctx := context.Background() 5335 5336 testCases := []struct { 5337 commit uint64 5338 progress []uint64 5339 brandNewReplicaID roachpb.ReplicaID 5340 expected []uint64 5341 }{ 5342 {1, []uint64{1, 0}, 2, []uint64{1}}, 5343 {1, []uint64{1, 0}, 1, nil}, 5344 {3, []uint64{3, 2, 1}, 3, []uint64{2}}, 5345 {3, []uint64{3, 2, 0}, 3, []uint64{2}}, 5346 {3, []uint64{4, 3, 2, 1}, 4, []uint64{4, 3, 2}}, 5347 {3, []uint64{4, 3, 2, 0}, 3, []uint64{4, 3, 0}}, 5348 {3, []uint64{4, 3, 2, 0}, 4, []uint64{4, 3, 2}}, 5349 } 5350 for _, c := range testCases { 5351 t.Run("", func(t *testing.T) { 5352 status := &raft.Status{ 5353 Progress: make(map[uint64]tracker.Progress), 5354 } 5355 // Use an invalid replica ID for the leader. TestFilterBehindReplicas covers 5356 // valid replica IDs. 5357 status.Lead = 99 5358 status.Commit = c.commit 5359 var replicas []roachpb.ReplicaDescriptor 5360 for j, v := range c.progress { 5361 p := tracker.Progress{ 5362 Match: v, 5363 State: tracker.StateReplicate, 5364 } 5365 if v == 0 { 5366 p.State = tracker.StateProbe 5367 } 5368 replicaID := uint64(j + 1) 5369 status.Progress[replicaID] = p 5370 replicas = append(replicas, roachpb.ReplicaDescriptor{ 5371 ReplicaID: roachpb.ReplicaID(replicaID), 5372 StoreID: roachpb.StoreID(v), 5373 }) 5374 } 5375 5376 candidates := simulateFilterUnremovableReplicas(ctx, status, replicas, c.brandNewReplicaID) 5377 var ids []uint64 5378 for _, c := range candidates { 5379 ids = append(ids, uint64(c.StoreID)) 5380 } 5381 if !reflect.DeepEqual(c.expected, ids) { 5382 t.Fatalf("expected %d, but got %d", c.expected, ids) 5383 } 5384 }) 5385 } 5386 } 5387 5388 // TestAllocatorRebalanceAway verifies that when a replica is on a node with a 5389 // bad zone config, the replica will be rebalanced off of it. 5390 func TestAllocatorRebalanceAway(t *testing.T) { 5391 defer leaktest.AfterTest(t)() 5392 5393 localityUS := roachpb.Locality{Tiers: []roachpb.Tier{{Key: "datacenter", Value: "us"}}} 5394 localityEUR := roachpb.Locality{Tiers: []roachpb.Tier{{Key: "datacenter", Value: "eur"}}} 5395 capacityEmpty := roachpb.StoreCapacity{Capacity: 100, Available: 99, RangeCount: 1} 5396 stores := []*roachpb.StoreDescriptor{ 5397 { 5398 StoreID: 1, 5399 Node: roachpb.NodeDescriptor{ 5400 NodeID: 1, 5401 Locality: localityUS, 5402 }, 5403 Capacity: capacityEmpty, 5404 }, 5405 { 5406 StoreID: 2, 5407 Node: roachpb.NodeDescriptor{ 5408 NodeID: 2, 5409 Locality: localityUS, 5410 }, 5411 Capacity: capacityEmpty, 5412 }, 5413 { 5414 StoreID: 3, 5415 Node: roachpb.NodeDescriptor{ 5416 NodeID: 3, 5417 Locality: localityEUR, 5418 }, 5419 Capacity: capacityEmpty, 5420 }, 5421 { 5422 StoreID: 4, 5423 Node: roachpb.NodeDescriptor{ 5424 NodeID: 4, 5425 Locality: localityUS, 5426 }, 5427 Capacity: capacityEmpty, 5428 }, 5429 { 5430 StoreID: 5, 5431 Node: roachpb.NodeDescriptor{ 5432 NodeID: 5, 5433 Locality: localityEUR, 5434 }, 5435 Capacity: capacityEmpty, 5436 }, 5437 } 5438 5439 existingReplicas := []roachpb.ReplicaDescriptor{ 5440 {StoreID: stores[0].StoreID}, 5441 {StoreID: stores[1].StoreID}, 5442 {StoreID: stores[2].StoreID}, 5443 } 5444 testCases := []struct { 5445 constraint zonepb.Constraint 5446 expected *roachpb.StoreID 5447 }{ 5448 { 5449 constraint: zonepb.Constraint{Key: "datacenter", Value: "us", Type: zonepb.Constraint_REQUIRED}, 5450 expected: &stores[3].StoreID, 5451 }, 5452 { 5453 constraint: zonepb.Constraint{Key: "datacenter", Value: "eur", Type: zonepb.Constraint_PROHIBITED}, 5454 expected: &stores[3].StoreID, 5455 }, 5456 { 5457 constraint: zonepb.Constraint{Key: "datacenter", Value: "eur", Type: zonepb.Constraint_REQUIRED}, 5458 expected: &stores[4].StoreID, 5459 }, 5460 { 5461 constraint: zonepb.Constraint{Key: "datacenter", Value: "us", Type: zonepb.Constraint_PROHIBITED}, 5462 expected: &stores[4].StoreID, 5463 }, 5464 { 5465 constraint: zonepb.Constraint{Key: "datacenter", Value: "other", Type: zonepb.Constraint_REQUIRED}, 5466 expected: nil, 5467 }, 5468 { 5469 constraint: zonepb.Constraint{Key: "datacenter", Value: "other", Type: zonepb.Constraint_PROHIBITED}, 5470 expected: nil, 5471 }, 5472 { 5473 constraint: zonepb.Constraint{Key: "datacenter", Value: "other", Type: zonepb.Constraint_DEPRECATED_POSITIVE}, 5474 expected: nil, 5475 }, 5476 { 5477 constraint: zonepb.Constraint{Key: "datacenter", Value: "us", Type: zonepb.Constraint_DEPRECATED_POSITIVE}, 5478 expected: nil, 5479 }, 5480 { 5481 constraint: zonepb.Constraint{Key: "datacenter", Value: "eur", Type: zonepb.Constraint_DEPRECATED_POSITIVE}, 5482 expected: nil, 5483 }, 5484 } 5485 5486 stopper, g, _, a, _ := createTestAllocator(10, false /* deterministic */) 5487 defer stopper.Stop(context.Background()) 5488 gossiputil.NewStoreGossiper(g).GossipStores(stores, t) 5489 ctx := context.Background() 5490 5491 for _, tc := range testCases { 5492 t.Run(tc.constraint.String(), func(t *testing.T) { 5493 constraints := zonepb.ConstraintsConjunction{ 5494 Constraints: []zonepb.Constraint{ 5495 tc.constraint, 5496 }, 5497 } 5498 5499 var rangeUsageInfo RangeUsageInfo 5500 actual, _, _, ok := a.RebalanceTarget( 5501 ctx, 5502 &zonepb.ZoneConfig{NumReplicas: proto.Int32(0), Constraints: []zonepb.ConstraintsConjunction{constraints}}, 5503 nil, 5504 existingReplicas, 5505 rangeUsageInfo, 5506 storeFilterThrottled, 5507 ) 5508 5509 if tc.expected == nil && ok { 5510 t.Errorf("rebalancing to the incorrect store, expected nil, got %d", actual.StoreID) 5511 } else if tc.expected != nil && !ok { 5512 t.Errorf("rebalancing to the incorrect store, expected %d, got nil", *tc.expected) 5513 } else if !(tc.expected == nil && !ok) && *tc.expected != actual.StoreID { 5514 t.Errorf("rebalancing to the incorrect store, expected %d, got %d", tc.expected, actual.StoreID) 5515 } 5516 }) 5517 } 5518 } 5519 5520 type testStore struct { 5521 roachpb.StoreDescriptor 5522 immediateCompaction bool 5523 } 5524 5525 func (ts *testStore) add(bytes int64) { 5526 ts.Capacity.RangeCount++ 5527 ts.Capacity.Available -= bytes 5528 ts.Capacity.Used += bytes 5529 ts.Capacity.LogicalBytes += bytes 5530 } 5531 5532 func (ts *testStore) rebalance(ots *testStore, bytes int64) { 5533 if ts.Capacity.RangeCount == 0 || (ts.Capacity.Capacity-ts.Capacity.Available) < bytes { 5534 return 5535 } 5536 // Mimic a real Store's behavior of rejecting preemptive snapshots when full. 5537 if !maxCapacityCheck(ots.StoreDescriptor) { 5538 log.Infof(context.Background(), 5539 "s%d too full to accept snapshot from s%d: %v", ots.StoreID, ts.StoreID, ots.Capacity) 5540 return 5541 } 5542 log.Infof(context.Background(), "s%d accepting snapshot from s%d", ots.StoreID, ts.StoreID) 5543 ts.Capacity.RangeCount-- 5544 if ts.immediateCompaction { 5545 ts.Capacity.Available += bytes 5546 ts.Capacity.Used -= bytes 5547 } 5548 ts.Capacity.LogicalBytes -= bytes 5549 ots.Capacity.RangeCount++ 5550 ots.Capacity.Available -= bytes 5551 ots.Capacity.Used += bytes 5552 ots.Capacity.LogicalBytes += bytes 5553 } 5554 5555 func (ts *testStore) compact() { 5556 ts.Capacity.Used = ts.Capacity.LogicalBytes 5557 ts.Capacity.Available = ts.Capacity.Capacity - ts.Capacity.Used 5558 } 5559 5560 func TestAllocatorFullDisks(t *testing.T) { 5561 defer leaktest.AfterTest(t)() 5562 5563 ctx := context.Background() 5564 stopper := stop.NewStopper() 5565 defer stopper.Stop(ctx) 5566 5567 st := cluster.MakeTestingClusterSettings() 5568 clock := hlc.NewClock(hlc.UnixNano, time.Nanosecond) 5569 5570 // Model a set of stores in a cluster doing rebalancing, with ranges being 5571 // randomly added occasionally. 5572 rpcContext := rpc.NewContext( 5573 log.AmbientContext{Tracer: st.Tracer}, 5574 &base.Config{Insecure: true}, 5575 clock, 5576 stopper, 5577 st, 5578 ) 5579 server := rpc.NewServer(rpcContext) // never started 5580 g := gossip.NewTest(1, rpcContext, server, stopper, metric.NewRegistry(), zonepb.DefaultZoneConfigRef()) 5581 5582 TimeUntilStoreDead.Override(&st.SV, TestTimeUntilStoreDeadOff) 5583 5584 const generations = 100 5585 const nodes = 20 5586 const capacity = (1 << 30) + 1 5587 const rangeSize = 16 << 20 5588 5589 mockNodeLiveness := newMockNodeLiveness(kvserverpb.NodeLivenessStatus_LIVE) 5590 sp := NewStorePool( 5591 log.AmbientContext{Tracer: st.Tracer}, 5592 st, 5593 g, 5594 clock, 5595 func() int { 5596 return nodes 5597 }, 5598 mockNodeLiveness.nodeLivenessFunc, 5599 false, /* deterministic */ 5600 ) 5601 alloc := MakeAllocator(sp, func(string) (time.Duration, bool) { 5602 return 0, false 5603 }) 5604 5605 var wg sync.WaitGroup 5606 g.RegisterCallback(gossip.MakePrefixPattern(gossip.KeyStorePrefix), 5607 func(_ string, _ roachpb.Value) { wg.Done() }, 5608 // Redundant callbacks are required by this test. 5609 gossip.Redundant) 5610 5611 rangesPerNode := int(math.Floor(capacity * rebalanceToMaxFractionUsedThreshold / rangeSize)) 5612 rangesToAdd := rangesPerNode * nodes 5613 5614 // Initialize testStores. 5615 var testStores [nodes]testStore 5616 for i := 0; i < len(testStores); i++ { 5617 // Don't immediately reclaim disk space from removed ranges. This mimics 5618 // range deletions don't immediately reclaim disk space in rocksdb. 5619 testStores[i].immediateCompaction = false 5620 testStores[i].StoreID = roachpb.StoreID(i) 5621 testStores[i].Node = roachpb.NodeDescriptor{NodeID: roachpb.NodeID(i)} 5622 testStores[i].Capacity = roachpb.StoreCapacity{Capacity: capacity, Available: capacity} 5623 } 5624 // Initialize the cluster with a single range. 5625 testStores[0].add(rangeSize) 5626 rangesAdded := 1 5627 5628 for i := 0; i < generations; i++ { 5629 // First loop through test stores and randomly add data. 5630 for j := 0; j < len(testStores); j++ { 5631 if mockNodeLiveness.nodeLivenessFunc(roachpb.NodeID(j), time.Time{}, 0) == kvserverpb.NodeLivenessStatus_DEAD { 5632 continue 5633 } 5634 ts := &testStores[j] 5635 // Add [0,3) ranges to the node, simulating splits and data growth. 5636 toAdd := alloc.randGen.Intn(3) 5637 for k := 0; k < toAdd; k++ { 5638 if rangesAdded < rangesToAdd { 5639 ts.add(rangeSize) 5640 rangesAdded++ 5641 } 5642 } 5643 if ts.Capacity.Available <= 0 { 5644 t.Errorf("testStore %d ran out of space during generation %d (rangesAdded=%d/%d): %+v", 5645 j, i, rangesAdded, rangesToAdd, ts.Capacity) 5646 mockNodeLiveness.setNodeStatus(roachpb.NodeID(j), kvserverpb.NodeLivenessStatus_DEAD) 5647 } 5648 wg.Add(1) 5649 if err := g.AddInfoProto(gossip.MakeStoreKey(roachpb.StoreID(j)), &ts.StoreDescriptor, 0); err != nil { 5650 t.Fatal(err) 5651 } 5652 } 5653 wg.Wait() 5654 5655 // Loop through each store a number of times and maybe rebalance. 5656 for j := 0; j < 10; j++ { 5657 for k := 0; k < len(testStores); k++ { 5658 if mockNodeLiveness.nodeLivenessFunc(roachpb.NodeID(k), time.Time{}, 0) == kvserverpb.NodeLivenessStatus_DEAD { 5659 continue 5660 } 5661 ts := &testStores[k] 5662 // Rebalance until there's no more rebalancing to do. 5663 if ts.Capacity.RangeCount > 0 { 5664 var rangeUsageInfo RangeUsageInfo 5665 target, _, details, ok := alloc.RebalanceTarget( 5666 ctx, 5667 zonepb.EmptyCompleteZoneConfig(), 5668 nil, 5669 []roachpb.ReplicaDescriptor{{NodeID: ts.Node.NodeID, StoreID: ts.StoreID}}, 5670 rangeUsageInfo, 5671 storeFilterThrottled, 5672 ) 5673 if ok { 5674 if log.V(1) { 5675 log.Infof(ctx, "rebalancing to %v; details: %s", target, details) 5676 } 5677 testStores[k].rebalance(&testStores[int(target.StoreID)], rangeSize) 5678 } 5679 } 5680 // Gossip occasionally, as real Stores do when replicas move around. 5681 if j%3 == 2 { 5682 wg.Add(1) 5683 if err := g.AddInfoProto(gossip.MakeStoreKey(roachpb.StoreID(j)), &ts.StoreDescriptor, 0); err != nil { 5684 t.Fatal(err) 5685 } 5686 } 5687 } 5688 } 5689 5690 // Simulate rocksdb compactions freeing up disk space. 5691 for j := 0; j < len(testStores); j++ { 5692 if mockNodeLiveness.nodeLivenessFunc(roachpb.NodeID(j), time.Time{}, 0) != kvserverpb.NodeLivenessStatus_DEAD { 5693 ts := &testStores[j] 5694 if ts.Capacity.Available <= 0 { 5695 t.Errorf("testStore %d ran out of space during generation %d: %+v", j, i, ts.Capacity) 5696 mockNodeLiveness.setNodeStatus(roachpb.NodeID(j), kvserverpb.NodeLivenessStatus_DEAD) 5697 } else { 5698 ts.compact() 5699 } 5700 } 5701 } 5702 } 5703 } 5704 5705 func Example_rebalancing() { 5706 stopper := stop.NewStopper() 5707 defer stopper.Stop(context.Background()) 5708 5709 st := cluster.MakeTestingClusterSettings() 5710 clock := hlc.NewClock(hlc.UnixNano, time.Nanosecond) 5711 5712 // Model a set of stores in a cluster, 5713 // adding / rebalancing ranges of random sizes. 5714 rpcContext := rpc.NewContext( 5715 log.AmbientContext{Tracer: st.Tracer}, 5716 &base.Config{Insecure: true}, 5717 clock, 5718 stopper, 5719 st, 5720 ) 5721 server := rpc.NewServer(rpcContext) // never started 5722 g := gossip.NewTest(1, rpcContext, server, stopper, metric.NewRegistry(), zonepb.DefaultZoneConfigRef()) 5723 5724 TimeUntilStoreDead.Override(&st.SV, TestTimeUntilStoreDeadOff) 5725 5726 const generations = 100 5727 const nodes = 20 5728 const printGenerations = generations / 2 5729 5730 // Deterministic must be set as this test is comparing the exact output 5731 // after each rebalance. 5732 sp := NewStorePool( 5733 log.AmbientContext{Tracer: st.Tracer}, 5734 st, 5735 g, 5736 clock, 5737 func() int { 5738 return nodes 5739 }, 5740 newMockNodeLiveness(kvserverpb.NodeLivenessStatus_LIVE).nodeLivenessFunc, 5741 /* deterministic */ true, 5742 ) 5743 alloc := MakeAllocator(sp, func(string) (time.Duration, bool) { 5744 return 0, false 5745 }) 5746 5747 var wg sync.WaitGroup 5748 g.RegisterCallback(gossip.MakePrefixPattern(gossip.KeyStorePrefix), 5749 func(_ string, _ roachpb.Value) { wg.Done() }, 5750 // Redundant callbacks are required by this test. 5751 gossip.Redundant) 5752 5753 // Initialize testStores. 5754 var testStores [nodes]testStore 5755 for i := 0; i < len(testStores); i++ { 5756 testStores[i].immediateCompaction = true 5757 testStores[i].StoreID = roachpb.StoreID(i) 5758 testStores[i].Node = roachpb.NodeDescriptor{NodeID: roachpb.NodeID(i)} 5759 testStores[i].Capacity = roachpb.StoreCapacity{Capacity: 1 << 30, Available: 1 << 30} 5760 } 5761 // Initialize the cluster with a single range. 5762 testStores[0].add(alloc.randGen.Int63n(1 << 20)) 5763 5764 table := tablewriter.NewWriter(os.Stdout) 5765 table.SetAutoFormatHeaders(false) 5766 table.SetAlignment(tablewriter.ALIGN_RIGHT) 5767 5768 header := make([]string, len(testStores)+1) 5769 header[0] = "gen" 5770 for i := 0; i < len(testStores); i++ { 5771 header[i+1] = fmt.Sprintf("store %d", i) 5772 } 5773 table.SetHeader(header) 5774 5775 for i := 0; i < generations; i++ { 5776 // First loop through test stores and add data. 5777 wg.Add(len(testStores)) 5778 for j := 0; j < len(testStores); j++ { 5779 // Add a pretend range to the testStore if there's already one. 5780 if testStores[j].Capacity.RangeCount > 0 { 5781 testStores[j].add(alloc.randGen.Int63n(1 << 20)) 5782 } 5783 if err := g.AddInfoProto(gossip.MakeStoreKey(roachpb.StoreID(j)), &testStores[j].StoreDescriptor, 0); err != nil { 5784 panic(err) 5785 } 5786 } 5787 wg.Wait() 5788 5789 // Next loop through test stores and maybe rebalance. 5790 for j := 0; j < len(testStores); j++ { 5791 ts := &testStores[j] 5792 var rangeUsageInfo RangeUsageInfo 5793 target, _, details, ok := alloc.RebalanceTarget( 5794 context.Background(), 5795 zonepb.EmptyCompleteZoneConfig(), 5796 nil, 5797 []roachpb.ReplicaDescriptor{{NodeID: ts.Node.NodeID, StoreID: ts.StoreID}}, 5798 rangeUsageInfo, 5799 storeFilterThrottled, 5800 ) 5801 if ok { 5802 log.Infof(context.Background(), "rebalancing to %v; details: %s", target, details) 5803 testStores[j].rebalance(&testStores[int(target.StoreID)], alloc.randGen.Int63n(1<<20)) 5804 } 5805 } 5806 5807 if i%(generations/printGenerations) == 0 { 5808 var totalBytes int64 5809 for j := 0; j < len(testStores); j++ { 5810 totalBytes += testStores[j].Capacity.Capacity - testStores[j].Capacity.Available 5811 } 5812 row := make([]string, len(testStores)+1) 5813 row[0] = fmt.Sprintf("%d", i) 5814 for j := 0; j < len(testStores); j++ { 5815 ts := testStores[j] 5816 bytes := ts.Capacity.Capacity - ts.Capacity.Available 5817 row[j+1] = fmt.Sprintf("%3d %3d%%", ts.Capacity.RangeCount, (100*bytes)/totalBytes) 5818 } 5819 table.Append(row) 5820 } 5821 } 5822 5823 var totBytes int64 5824 var totRanges int32 5825 for i := 0; i < len(testStores); i++ { 5826 totBytes += testStores[i].Capacity.Capacity - testStores[i].Capacity.Available 5827 totRanges += testStores[i].Capacity.RangeCount 5828 } 5829 table.Render() 5830 fmt.Printf("Total bytes=%d, ranges=%d\n", totBytes, totRanges) 5831 5832 // Output: 5833 // +-----+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+ 5834 // | gen | store 0 | store 1 | store 2 | store 3 | store 4 | store 5 | store 6 | store 7 | store 8 | store 9 | store 10 | store 11 | store 12 | store 13 | store 14 | store 15 | store 16 | store 17 | store 18 | store 19 | 5835 // +-----+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+ 5836 // | 0 | 2 100% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 5837 // | 2 | 4 100% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 5838 // | 4 | 6 100% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 5839 // | 6 | 8 100% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 5840 // | 8 | 10 100% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 5841 // | 10 | 10 68% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 1 11% | 0 0% | 2 20% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 5842 // | 12 | 10 34% | 1 2% | 0 0% | 2 18% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 3 14% | 0 0% | 3 27% | 0 0% | 0 0% | 1 2% | 0 0% | 0 0% | 0 0% | 5843 // | 14 | 10 22% | 3 7% | 0 0% | 4 18% | 0 0% | 0 0% | 0 0% | 0 0% | 0 0% | 1 3% | 0 0% | 4 11% | 2 1% | 4 22% | 0 0% | 0 0% | 3 8% | 1 0% | 1 4% | 0 0% | 5844 // | 16 | 10 12% | 5 10% | 0 0% | 5 13% | 0 0% | 2 4% | 1 3% | 2 1% | 0 0% | 3 6% | 1 0% | 5 8% | 4 5% | 5 12% | 0 0% | 0 0% | 5 9% | 3 4% | 3 8% | 0 0% | 5845 // | 18 | 10 5% | 6 7% | 3 1% | 6 9% | 3 4% | 4 6% | 3 5% | 4 2% | 2 1% | 4 5% | 3 2% | 6 7% | 5 3% | 6 10% | 3 2% | 4 3% | 6 6% | 4 5% | 4 6% | 2 1% | 5846 // | 20 | 10 4% | 8 6% | 5 2% | 8 8% | 5 4% | 6 6% | 5 5% | 6 3% | 5 3% | 6 6% | 5 2% | 8 6% | 7 4% | 8 9% | 5 3% | 6 4% | 8 6% | 6 3% | 6 5% | 5 3% | 5847 // | 22 | 11 4% | 10 6% | 7 2% | 10 7% | 7 4% | 8 6% | 7 5% | 8 4% | 7 3% | 8 5% | 8 2% | 10 6% | 9 4% | 10 8% | 7 3% | 8 3% | 10 6% | 8 3% | 8 5% | 7 4% | 5848 // | 24 | 13 5% | 12 6% | 9 2% | 12 6% | 9 4% | 10 5% | 9 5% | 10 4% | 9 4% | 10 5% | 10 3% | 12 6% | 11 4% | 12 7% | 9 3% | 10 3% | 12 6% | 10 3% | 10 5% | 9 4% | 5849 // | 26 | 15 5% | 14 6% | 11 3% | 14 6% | 11 4% | 12 5% | 11 4% | 12 4% | 11 4% | 12 5% | 12 3% | 14 6% | 13 4% | 14 6% | 11 4% | 12 3% | 14 6% | 12 3% | 12 5% | 11 4% | 5850 // | 28 | 17 5% | 16 5% | 13 4% | 16 6% | 13 4% | 14 5% | 13 4% | 14 4% | 13 4% | 14 5% | 14 3% | 16 5% | 15 5% | 16 6% | 13 4% | 14 3% | 16 5% | 14 4% | 14 4% | 13 4% | 5851 // | 30 | 19 5% | 18 6% | 15 3% | 18 6% | 15 4% | 16 5% | 15 4% | 16 4% | 15 4% | 16 5% | 16 3% | 18 5% | 17 5% | 18 5% | 15 4% | 16 3% | 18 6% | 16 4% | 16 5% | 15 4% | 5852 // | 32 | 21 4% | 20 5% | 17 4% | 20 6% | 17 4% | 18 6% | 17 5% | 18 4% | 17 4% | 18 5% | 18 3% | 20 5% | 19 4% | 20 6% | 17 4% | 18 3% | 20 6% | 18 4% | 18 4% | 17 4% | 5853 // | 34 | 23 4% | 22 6% | 19 4% | 22 6% | 19 4% | 20 5% | 19 5% | 20 4% | 19 4% | 20 4% | 20 4% | 22 5% | 21 4% | 22 5% | 19 4% | 20 3% | 22 6% | 20 3% | 20 4% | 19 5% | 5854 // | 36 | 25 4% | 24 5% | 21 4% | 24 7% | 21 4% | 22 5% | 21 5% | 22 4% | 21 4% | 22 4% | 22 4% | 24 5% | 23 4% | 24 5% | 21 4% | 22 4% | 24 6% | 22 4% | 22 4% | 21 5% | 5855 // | 38 | 27 4% | 26 5% | 23 4% | 26 6% | 23 4% | 24 5% | 23 5% | 24 4% | 23 4% | 24 4% | 24 4% | 26 5% | 25 5% | 26 5% | 23 4% | 24 4% | 26 6% | 24 4% | 24 5% | 23 5% | 5856 // | 40 | 29 4% | 28 5% | 25 4% | 28 6% | 25 4% | 26 5% | 25 5% | 26 4% | 25 4% | 26 4% | 26 4% | 28 5% | 27 4% | 28 5% | 25 4% | 26 4% | 28 5% | 26 4% | 26 4% | 25 5% | 5857 // | 42 | 31 4% | 30 5% | 27 4% | 30 6% | 27 4% | 28 5% | 27 5% | 28 4% | 27 4% | 28 4% | 28 4% | 30 5% | 29 4% | 30 5% | 27 4% | 28 4% | 30 5% | 28 4% | 28 4% | 27 5% | 5858 // | 44 | 33 4% | 32 5% | 29 4% | 32 6% | 29 4% | 30 5% | 29 5% | 30 3% | 29 4% | 30 4% | 30 4% | 32 5% | 31 4% | 32 6% | 29 4% | 30 4% | 32 5% | 30 4% | 30 4% | 29 5% | 5859 // | 46 | 35 4% | 34 5% | 31 4% | 34 6% | 31 4% | 32 5% | 31 5% | 32 3% | 31 4% | 32 5% | 32 4% | 34 5% | 33 4% | 34 6% | 31 4% | 32 4% | 34 5% | 32 4% | 32 4% | 31 5% | 5860 // | 48 | 37 4% | 36 4% | 33 4% | 36 5% | 33 4% | 34 5% | 33 5% | 34 3% | 33 4% | 34 5% | 34 4% | 36 5% | 35 4% | 36 6% | 33 4% | 34 4% | 36 5% | 34 4% | 34 5% | 33 5% | 5861 // | 50 | 39 4% | 38 4% | 35 4% | 38 5% | 35 4% | 36 5% | 35 5% | 36 3% | 35 4% | 36 5% | 36 4% | 38 5% | 37 4% | 38 5% | 35 4% | 36 4% | 38 5% | 36 4% | 36 5% | 35 5% | 5862 // | 52 | 41 4% | 40 5% | 37 4% | 40 5% | 37 4% | 38 5% | 37 5% | 38 3% | 37 4% | 38 5% | 38 4% | 40 5% | 39 4% | 40 5% | 37 5% | 38 4% | 40 5% | 38 4% | 38 5% | 37 5% | 5863 // | 54 | 43 4% | 42 5% | 39 4% | 42 5% | 39 4% | 40 5% | 39 5% | 40 3% | 39 4% | 40 5% | 40 4% | 42 5% | 41 4% | 42 5% | 39 5% | 40 4% | 42 5% | 40 4% | 40 5% | 39 5% | 5864 // | 56 | 45 4% | 44 5% | 41 4% | 44 5% | 41 4% | 42 5% | 41 5% | 42 4% | 41 4% | 42 5% | 42 4% | 44 5% | 43 4% | 44 5% | 41 5% | 42 4% | 44 5% | 42 4% | 42 5% | 41 5% | 5865 // | 58 | 47 4% | 46 5% | 43 4% | 46 5% | 43 4% | 44 5% | 43 5% | 44 4% | 43 4% | 44 5% | 44 4% | 46 5% | 45 5% | 46 5% | 43 5% | 44 4% | 46 5% | 44 4% | 44 5% | 43 5% | 5866 // | 60 | 49 4% | 48 5% | 45 3% | 48 5% | 45 4% | 46 5% | 45 5% | 46 4% | 45 4% | 46 5% | 46 4% | 48 5% | 47 5% | 48 5% | 45 5% | 46 4% | 48 5% | 46 5% | 46 4% | 45 5% | 5867 // | 62 | 51 4% | 50 5% | 47 4% | 50 5% | 47 4% | 48 5% | 47 5% | 48 4% | 47 4% | 48 5% | 48 5% | 50 5% | 49 5% | 50 5% | 47 5% | 48 4% | 50 5% | 48 5% | 48 4% | 47 5% | 5868 // | 64 | 53 4% | 52 5% | 49 3% | 52 5% | 49 4% | 50 5% | 49 5% | 50 4% | 49 4% | 50 5% | 50 5% | 52 5% | 51 5% | 52 5% | 49 5% | 50 4% | 52 5% | 50 4% | 50 4% | 49 5% | 5869 // | 66 | 55 4% | 54 5% | 51 4% | 54 5% | 51 4% | 52 5% | 51 5% | 52 4% | 51 4% | 52 5% | 52 5% | 54 5% | 53 5% | 54 5% | 51 5% | 52 5% | 54 5% | 52 4% | 52 4% | 51 5% | 5870 // | 68 | 57 4% | 56 5% | 53 4% | 56 5% | 53 4% | 54 5% | 53 5% | 54 4% | 53 4% | 54 5% | 54 5% | 56 5% | 55 5% | 56 5% | 53 5% | 54 5% | 56 5% | 54 4% | 54 4% | 53 5% | 5871 // | 70 | 59 4% | 58 5% | 55 4% | 58 5% | 55 4% | 56 5% | 55 5% | 56 4% | 55 4% | 56 5% | 56 4% | 58 5% | 57 5% | 58 5% | 55 5% | 56 5% | 58 5% | 56 4% | 56 4% | 55 5% | 5872 // | 72 | 61 4% | 60 5% | 57 4% | 60 5% | 57 4% | 58 5% | 57 5% | 58 4% | 57 4% | 58 5% | 58 4% | 60 5% | 59 5% | 60 5% | 57 4% | 58 5% | 60 5% | 58 5% | 58 4% | 57 5% | 5873 // | 74 | 63 4% | 62 5% | 59 4% | 62 5% | 59 4% | 60 5% | 59 5% | 60 4% | 59 4% | 60 5% | 60 4% | 62 5% | 61 5% | 62 5% | 59 4% | 60 5% | 62 5% | 60 5% | 60 4% | 59 5% | 5874 // | 76 | 65 4% | 64 5% | 61 4% | 64 5% | 61 4% | 62 5% | 61 5% | 62 4% | 61 4% | 62 5% | 62 4% | 64 5% | 63 5% | 64 5% | 61 5% | 62 5% | 64 4% | 62 5% | 62 4% | 61 5% | 5875 // | 78 | 67 4% | 66 5% | 63 4% | 66 5% | 63 4% | 64 5% | 63 5% | 64 4% | 63 4% | 64 5% | 64 4% | 66 5% | 65 5% | 66 5% | 63 4% | 64 5% | 66 5% | 64 5% | 64 4% | 63 5% | 5876 // | 80 | 69 4% | 68 5% | 65 4% | 68 5% | 65 4% | 66 5% | 65 5% | 66 4% | 65 4% | 66 5% | 66 4% | 68 5% | 67 4% | 68 5% | 65 4% | 66 5% | 68 5% | 66 5% | 66 4% | 65 5% | 5877 // | 82 | 71 4% | 70 5% | 67 4% | 70 5% | 67 4% | 68 5% | 67 5% | 68 4% | 67 4% | 68 5% | 68 4% | 70 5% | 69 5% | 70 5% | 67 4% | 68 4% | 70 5% | 68 4% | 68 4% | 67 5% | 5878 // | 84 | 73 4% | 72 5% | 69 4% | 72 5% | 69 4% | 70 5% | 69 5% | 70 4% | 69 4% | 70 5% | 70 4% | 72 5% | 71 4% | 72 5% | 69 4% | 70 4% | 72 5% | 70 4% | 70 4% | 69 5% | 5879 // | 86 | 75 4% | 74 5% | 71 4% | 74 5% | 71 4% | 72 5% | 71 5% | 72 4% | 71 4% | 72 5% | 72 4% | 74 5% | 73 4% | 74 5% | 71 4% | 72 4% | 74 5% | 72 4% | 72 4% | 71 5% | 5880 // | 88 | 77 4% | 76 4% | 73 4% | 76 5% | 73 4% | 74 5% | 73 5% | 74 4% | 73 4% | 74 5% | 74 4% | 76 5% | 75 4% | 76 5% | 73 4% | 74 4% | 76 5% | 74 4% | 74 4% | 73 5% | 5881 // | 90 | 79 4% | 78 4% | 75 4% | 78 5% | 75 4% | 76 5% | 75 5% | 76 4% | 75 4% | 76 5% | 76 4% | 78 5% | 77 5% | 78 5% | 75 5% | 76 4% | 78 5% | 76 4% | 76 4% | 75 5% | 5882 // | 92 | 81 4% | 80 4% | 77 4% | 80 5% | 77 4% | 78 5% | 77 5% | 78 4% | 77 4% | 78 5% | 78 4% | 80 5% | 79 5% | 80 5% | 77 5% | 78 4% | 80 5% | 78 4% | 78 4% | 77 5% | 5883 // | 94 | 83 4% | 82 4% | 79 4% | 82 5% | 79 4% | 80 5% | 79 5% | 80 4% | 79 4% | 80 5% | 80 4% | 82 5% | 81 5% | 82 5% | 79 4% | 80 4% | 82 5% | 80 4% | 80 4% | 79 5% | 5884 // | 96 | 85 4% | 84 4% | 81 4% | 84 5% | 81 5% | 82 5% | 81 5% | 82 4% | 81 4% | 82 5% | 82 4% | 84 5% | 83 5% | 84 5% | 81 5% | 82 4% | 84 5% | 82 4% | 82 4% | 81 5% | 5885 // | 98 | 87 4% | 86 4% | 83 4% | 86 5% | 83 5% | 84 5% | 83 5% | 84 4% | 83 4% | 84 5% | 84 4% | 86 5% | 85 5% | 86 5% | 83 5% | 84 4% | 86 5% | 84 4% | 84 4% | 83 5% | 5886 // +-----+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+ 5887 // Total bytes=894061338, ranges=1708 5888 }