@@ -23,6 +23,7 @@ import (
23
23
"github.com/samber/lo"
24
24
"github.com/stretchr/testify/mock"
25
25
"github.com/stretchr/testify/suite"
26
+ "go.uber.org/atomic"
26
27
27
28
etcdkv "github.com/milvus-io/milvus/internal/kv/etcd"
28
29
"github.com/milvus-io/milvus/internal/metastore/kv/querycoord"
@@ -1470,3 +1471,127 @@ func (suite *ScoreBasedBalancerTestSuite) TestBalanceChannelOnChannelExclusive()
1470
1471
_ , channelPlans = suite .getCollectionBalancePlans (balancer , 3 )
1471
1472
suite .Len (channelPlans , 2 )
1472
1473
}
1474
+
1475
+ func (suite * ScoreBasedBalancerTestSuite ) TestBalanceChannelOnStoppingNode () {
1476
+ ctx := context .Background ()
1477
+ balancer := suite .balancer
1478
+
1479
+ // mock 10 collections with each collection has 1 channel
1480
+ collectionNum := 10
1481
+ channelNum := 1
1482
+ for i := 1 ; i <= collectionNum ; i ++ {
1483
+ collectionID := int64 (i )
1484
+ collection := utils .CreateTestCollection (collectionID , int32 (1 ))
1485
+ collection .LoadPercentage = 100
1486
+ collection .Status = querypb .LoadStatus_Loaded
1487
+ balancer .meta .CollectionManager .PutCollection (ctx , collection )
1488
+ balancer .meta .CollectionManager .PutPartition (ctx , utils .CreateTestPartition (collectionID , collectionID ))
1489
+ balancer .meta .ReplicaManager .Spawn (ctx , collectionID , map [string ]int {meta .DefaultResourceGroupName : 1 }, nil )
1490
+
1491
+ channels := make ([]* datapb.VchannelInfo , channelNum )
1492
+ for i := 0 ; i < channelNum ; i ++ {
1493
+ channels [i ] = & datapb.VchannelInfo {CollectionID : collectionID , ChannelName : fmt .Sprintf ("channel-%d-%d" , collectionID , i )}
1494
+ }
1495
+ suite .broker .EXPECT ().GetRecoveryInfoV2 (mock .Anything , collectionID ).Return (
1496
+ channels , nil , nil )
1497
+ suite .broker .EXPECT ().GetPartitions (mock .Anything , collectionID ).Return ([]int64 {collectionID }, nil ).Maybe ()
1498
+ balancer .targetMgr .UpdateCollectionNextTarget (ctx , collectionID )
1499
+ balancer .targetMgr .UpdateCollectionCurrentTarget (ctx , collectionID )
1500
+ }
1501
+
1502
+ // mock querynode-1 to node manager
1503
+ nodeInfo := session .NewNodeInfo (session.ImmutableNodeInfo {
1504
+ NodeID : 1 ,
1505
+ Address : "127.0.0.1:0" ,
1506
+ Hostname : "localhost" ,
1507
+ Version : common .Version ,
1508
+ })
1509
+ nodeInfo .SetState (session .NodeStateNormal )
1510
+ suite .balancer .nodeManager .Add (nodeInfo )
1511
+ suite .balancer .meta .ResourceManager .HandleNodeUp (ctx , 1 )
1512
+ utils .RecoverAllCollection (balancer .meta )
1513
+
1514
+ // mock channel distribution
1515
+ channelDist := make ([]* meta.DmChannel , 0 )
1516
+ for i := 1 ; i <= collectionNum ; i ++ {
1517
+ collectionID := int64 (i )
1518
+ for i := 0 ; i < channelNum ; i ++ {
1519
+ channelDist = append (channelDist , & meta.DmChannel {
1520
+ VchannelInfo : & datapb.VchannelInfo {CollectionID : collectionID , ChannelName : fmt .Sprintf ("channel-%d-%d" , collectionID , i )}, Node : 1 ,
1521
+ })
1522
+ }
1523
+ }
1524
+ balancer .dist .ChannelDistManager .Update (1 , channelDist ... )
1525
+
1526
+ // assert balance channel won't happens on 1 querynode
1527
+ ret := make ([]ChannelAssignPlan , 0 )
1528
+ for i := 1 ; i <= collectionNum ; i ++ {
1529
+ collectionID := int64 (i )
1530
+ _ , channelPlans := suite .getCollectionBalancePlans (balancer , collectionID )
1531
+ ret = append (ret , channelPlans ... )
1532
+ }
1533
+ suite .Len (ret , 0 )
1534
+
1535
+ // mock querynode-2 and querynode-3 to node manager
1536
+ nodeInfo2 := session .NewNodeInfo (session.ImmutableNodeInfo {
1537
+ NodeID : 2 ,
1538
+ Address : "127.0.0.1:0" ,
1539
+ Hostname : "localhost" ,
1540
+ Version : common .Version ,
1541
+ })
1542
+ suite .balancer .nodeManager .Add (nodeInfo2 )
1543
+ suite .balancer .meta .ResourceManager .HandleNodeUp (ctx , 2 )
1544
+ // mock querynode-2 and querynode-3 to node manager
1545
+ nodeInfo3 := session .NewNodeInfo (session.ImmutableNodeInfo {
1546
+ NodeID : 3 ,
1547
+ Address : "127.0.0.1:0" ,
1548
+ Hostname : "localhost" ,
1549
+ Version : common .Version ,
1550
+ })
1551
+ suite .balancer .nodeManager .Add (nodeInfo3 )
1552
+ suite .balancer .meta .ResourceManager .HandleNodeUp (ctx , 3 )
1553
+ utils .RecoverAllCollection (balancer .meta )
1554
+ // mock querynode-1 to stopping, trigger stopping balance, expect to generate 10 balance channel task, and 5 for node-2, 5 for node-3
1555
+ nodeInfo .SetState (session .NodeStateStopping )
1556
+ suite .balancer .meta .ResourceManager .HandleNodeDown (ctx , 1 )
1557
+ utils .RecoverAllCollection (balancer .meta )
1558
+
1559
+ node2Counter := atomic .NewInt32 (0 )
1560
+ node3Counter := atomic .NewInt32 (0 )
1561
+
1562
+ suite .mockScheduler .ExpectedCalls = nil
1563
+ suite .mockScheduler .EXPECT ().GetSegmentTaskDelta (mock .Anything , mock .Anything ).Return (0 ).Maybe ()
1564
+ suite .mockScheduler .EXPECT ().GetChannelTaskDelta (mock .Anything , mock .Anything ).RunAndReturn (func (nodeID , collection int64 ) int {
1565
+ if collection == - 1 {
1566
+ if nodeID == 2 {
1567
+ return int (node2Counter .Load ())
1568
+ }
1569
+
1570
+ if nodeID == 3 {
1571
+ return int (node3Counter .Load ())
1572
+ }
1573
+ }
1574
+ return 0
1575
+ })
1576
+ suite .mockScheduler .EXPECT ().GetSegmentTaskNum (mock .Anything , mock .Anything ).Return (0 ).Maybe ()
1577
+ suite .mockScheduler .EXPECT ().GetChannelTaskNum (mock .Anything , mock .Anything ).Return (0 ).Maybe ()
1578
+
1579
+ for i := 1 ; i <= collectionNum ; i ++ {
1580
+ collectionID := int64 (i )
1581
+ _ , channelPlans := suite .getCollectionBalancePlans (balancer , collectionID )
1582
+ suite .Len (channelPlans , 1 )
1583
+ if channelPlans [0 ].To == 2 {
1584
+ node2Counter .Inc ()
1585
+ }
1586
+
1587
+ if channelPlans [0 ].To == 3 {
1588
+ node3Counter .Inc ()
1589
+ }
1590
+
1591
+ if i % 2 == 0 {
1592
+ suite .Equal (node2Counter .Load (), node3Counter .Load ())
1593
+ }
1594
+ }
1595
+ suite .Equal (node2Counter .Load (), int32 (5 ))
1596
+ suite .Equal (node3Counter .Load (), int32 (5 ))
1597
+ }
0 commit comments