Skip to content

Commit 03f862a

Browse files
committed
allocatorimpl: add TestAllocatorRebalanceMMAConflict
This commit adds TestAllocatorRebalanceMMAConflict, which verifies that allocator.RebalanceTarget allows critical rebalances to proceed even when they conflict with MMA's goals, and ensures it consults MMA correctly for non-critical rebalances.
1 parent 4363353 commit 03f862a

File tree

1 file changed

+219
-0
lines changed

1 file changed

+219
-0
lines changed

pkg/kv/kvserver/allocator/allocatorimpl/allocator_test.go

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4959,6 +4959,225 @@ func TestAllocatorRebalanceIOOverloadCheck(t *testing.T) {
49594959
}
49604960
}
49614961

4962+
// TestAllocatorRebalanceMMAConflict tests that the MMA conflict checking logic
4963+
// in RebalanceVoter works correctly. It should skip MMA conflict checks for
4964+
// critical rebalances (constraint repair, disk fullness, diversity
4965+
// improvements) but apply them if it's just for range count based rebalancing.
4966+
func TestAllocatorRebalanceMMAConflict(t *testing.T) {
4967+
defer leaktest.AfterTest(t)()
4968+
ctx := context.Background()
4969+
4970+
type testCase struct {
4971+
name string
4972+
mmaReturnsConflict bool
4973+
stores []*roachpb.StoreDescriptor
4974+
conf *roachpb.SpanConfig
4975+
existingVoters []roachpb.ReplicaDescriptor
4976+
expectNoAction bool
4977+
expectedRemoveTarget roachpb.StoreID
4978+
expectedAddTarget roachpb.StoreID
4979+
}
4980+
4981+
// Stores with locality constraints for testing.
4982+
constraintRepairConfig := &roachpb.SpanConfig{
4983+
NumReplicas: 1,
4984+
Constraints: []roachpb.ConstraintsConjunction{{
4985+
Constraints: []roachpb.Constraint{{Key: "region", Value: "us-east", Type: roachpb.Constraint_REQUIRED}},
4986+
}},
4987+
}
4988+
constraintRepairStores := []*roachpb.StoreDescriptor{
4989+
{
4990+
StoreID: 1,
4991+
Node: roachpb.NodeDescriptor{
4992+
NodeID: 1,
4993+
Locality: roachpb.Locality{
4994+
Tiers: []roachpb.Tier{
4995+
{Key: "region", Value: "us-west"},
4996+
},
4997+
},
4998+
},
4999+
Capacity: roachpb.StoreCapacity{Capacity: 200, Available: 100, LogicalBytes: 100, RangeCount: 40},
5000+
},
5001+
{
5002+
StoreID: 2,
5003+
Node: roachpb.NodeDescriptor{
5004+
NodeID: 2,
5005+
Locality: roachpb.Locality{
5006+
Tiers: []roachpb.Tier{
5007+
{Key: "region", Value: "us-east"}, // Has required locality
5008+
},
5009+
},
5010+
},
5011+
Capacity: roachpb.StoreCapacity{Capacity: 200, Available: 100, LogicalBytes: 100, RangeCount: 50},
5012+
},
5013+
}
5014+
5015+
// Stores with disk fullness for testing.
5016+
diskFullnessStores := []*roachpb.StoreDescriptor{
5017+
{
5018+
StoreID: 1,
5019+
Node: roachpb.NodeDescriptor{
5020+
NodeID: 1,
5021+
},
5022+
Capacity: roachpb.StoreCapacity{Capacity: 1000, Available: 10, LogicalBytes: 990, RangeCount: 40}, // Nearly full
5023+
},
5024+
{
5025+
StoreID: 2,
5026+
Node: roachpb.NodeDescriptor{
5027+
NodeID: 2,
5028+
},
5029+
Capacity: roachpb.StoreCapacity{Capacity: 1000, Available: 500, LogicalBytes: 500, RangeCount: 50}, // Plenty of space
5030+
},
5031+
}
5032+
5033+
// Stores for testing range count rebalancing.
5034+
rangeCountStores := []*roachpb.StoreDescriptor{
5035+
{
5036+
StoreID: 1,
5037+
Node: roachpb.NodeDescriptor{
5038+
NodeID: 1,
5039+
},
5040+
Capacity: roachpb.StoreCapacity{Capacity: 1000, Available: 500, LogicalBytes: 500, RangeCount: 100}, // High range count
5041+
},
5042+
{
5043+
StoreID: 2,
5044+
Node: roachpb.NodeDescriptor{
5045+
NodeID: 2,
5046+
},
5047+
Capacity: roachpb.StoreCapacity{Capacity: 1000, Available: 500, LogicalBytes: 500, RangeCount: 50}, // Low range count
5048+
},
5049+
}
5050+
5051+
// Stores for testing diversity improvements - different datacenters for
5052+
// better diversity.
5053+
diversityStores := []*roachpb.StoreDescriptor{
5054+
{
5055+
StoreID: 1,
5056+
Node: roachpb.NodeDescriptor{
5057+
NodeID: 1,
5058+
Locality: roachpb.Locality{
5059+
Tiers: []roachpb.Tier{
5060+
{Key: "datacenter", Value: "dc1"},
5061+
},
5062+
},
5063+
},
5064+
Capacity: roachpb.StoreCapacity{Capacity: 1000, Available: 500, LogicalBytes: 500, RangeCount: 50},
5065+
},
5066+
{
5067+
StoreID: 2,
5068+
Node: roachpb.NodeDescriptor{
5069+
NodeID: 2,
5070+
Locality: roachpb.Locality{
5071+
Tiers: []roachpb.Tier{
5072+
{Key: "datacenter", Value: "dc2"}, // Different datacenter for better diversity
5073+
},
5074+
},
5075+
},
5076+
Capacity: roachpb.StoreCapacity{Capacity: 1000, Available: 500, LogicalBytes: 500, RangeCount: 50},
5077+
},
5078+
{
5079+
StoreID: 3,
5080+
Node: roachpb.NodeDescriptor{
5081+
NodeID: 3,
5082+
Locality: roachpb.Locality{
5083+
Tiers: []roachpb.Tier{
5084+
{Key: "datacenter", Value: "dc1"}, // Same datacenter as store 1, poor diversity
5085+
},
5086+
},
5087+
},
5088+
Capacity: roachpb.StoreCapacity{Capacity: 1000, Available: 500, LogicalBytes: 500, RangeCount: 50},
5089+
},
5090+
}
5091+
5092+
tests := []testCase{
5093+
{
5094+
name: "constraint repair bypasses MMA conflict",
5095+
mmaReturnsConflict: true, // MMA says conflict, but should be ignored
5096+
stores: constraintRepairStores,
5097+
conf: constraintRepairConfig,
5098+
existingVoters: replicas(1),
5099+
expectedRemoveTarget: 1,
5100+
expectedAddTarget: 2,
5101+
},
5102+
{
5103+
name: "disk fullness bypasses MMA conflict",
5104+
mmaReturnsConflict: true, // MMA says conflict, but should be ignored
5105+
stores: diskFullnessStores,
5106+
conf: emptySpanConfig(),
5107+
existingVoters: replicas(1),
5108+
expectedRemoveTarget: 1,
5109+
expectedAddTarget: 2,
5110+
},
5111+
{
5112+
name: "diversity improvement bypasses MMA conflict",
5113+
mmaReturnsConflict: true, // MMA says conflict, but should be ignored for diversity
5114+
stores: diversityStores,
5115+
conf: emptySpanConfig(),
5116+
existingVoters: replicas(1, 3), // Both replicas in dc1, poor diversity
5117+
expectedRemoveTarget: 3, // Remove one from dc1
5118+
expectedAddTarget: 2, // Add to dc2 for better diversity
5119+
},
5120+
{
5121+
name: "range count rebalancing respects MMA conflict",
5122+
mmaReturnsConflict: true, // MMA says conflict, should be respected
5123+
stores: rangeCountStores,
5124+
conf: emptySpanConfig(),
5125+
existingVoters: replicas(1),
5126+
expectNoAction: true, // Should be blocked by MMA conflict
5127+
},
5128+
{
5129+
name: "range count rebalancing proceeds when MMA allows",
5130+
mmaReturnsConflict: false, // MMA says no conflict
5131+
stores: rangeCountStores,
5132+
conf: emptySpanConfig(),
5133+
existingVoters: replicas(1),
5134+
expectedRemoveTarget: 1,
5135+
expectedAddTarget: 2,
5136+
},
5137+
}
5138+
5139+
var rangeUsageInfo allocator.RangeUsageInfo
5140+
5141+
for i, test := range tests {
5142+
t.Run(fmt.Sprintf("%d_%s", i+1, test.name), func(t *testing.T) {
5143+
fmt.Println("test.name", test.name)
5144+
mmaKnobs := &mmaintegration.TestingKnobs{
5145+
OverrideIsInConflictWithMMA: func(cand roachpb.StoreID) bool {
5146+
return test.mmaReturnsConflict
5147+
},
5148+
}
5149+
stopper, g, sp, a, _ := CreateTestAllocatorWithKnobs(
5150+
ctx, 5, true /* deterministic */, nil /* allocatorKnobs */, mmaKnobs)
5151+
defer stopper.Stop(ctx)
5152+
sg := gossiputil.NewStoreGossiper(g)
5153+
sg.GossipStores(test.stores, t)
5154+
add, remove, _, ok := a.RebalanceVoter(
5155+
ctx,
5156+
sp,
5157+
test.conf,
5158+
nil,
5159+
test.existingVoters,
5160+
[]roachpb.ReplicaDescriptor{},
5161+
rangeUsageInfo,
5162+
storepool.StoreFilterThrottled,
5163+
a.ScorerOptions(ctx),
5164+
)
5165+
5166+
if test.expectNoAction {
5167+
require.Falsef(t, ok, "expected no rebalance action but got one")
5168+
} else {
5169+
require.Truef(t, ok, "expected rebalance action but got none")
5170+
require.Equalf(t, test.expectedAddTarget, add.StoreID,
5171+
"the addition target %+v from RebalanceVoter doesn't match expectation %v",
5172+
add, test.expectedAddTarget)
5173+
require.Equalf(t, test.expectedRemoveTarget, remove.StoreID,
5174+
"the removal target %+v from RebalanceVoter doesn't match expectation %v",
5175+
remove, test.expectedRemoveTarget)
5176+
}
5177+
})
5178+
}
5179+
}
5180+
49625181
// TestVotersCanRebalanceToNonVoterStores ensures that rebalancing of voting
49635182
// replicas considers stores that have non-voters as feasible candidates.
49645183
func TestVotersCanRebalanceToNonVoterStores(t *testing.T) {

0 commit comments

Comments
 (0)