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  }