// Licensed to the LF AI & Data foundation under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package datacoord import ( "context" "fmt" "testing" "github.com/golang/protobuf/proto" "github.com/samber/lo" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" "go.uber.org/zap" kvmock "github.com/milvus-io/milvus/internal/kv/mocks" "github.com/milvus-io/milvus/internal/kv/predicates" "github.com/milvus-io/milvus/internal/proto/datapb" "github.com/milvus-io/milvus/pkg/log" "github.com/milvus-io/milvus/pkg/util/paramtable" ) func TestChannelManagerSuite(t *testing.T) { suite.Run(t, new(ChannelManagerSuite)) } type ChannelManagerSuite struct { suite.Suite mockKv *kvmock.MetaKv mockCluster *MockSubCluster mockAlloc *NMockAllocator mockHandler *NMockHandler } func (s *ChannelManagerSuite) prepareMeta(chNodes map[string]int64, state datapb.ChannelWatchState) { s.SetupTest() if chNodes == nil { s.mockKv.EXPECT().LoadWithPrefix(mock.Anything).Return(nil, nil, nil).Once() return } var keys, values []string for channel, nodeID := range chNodes { keys = append(keys, fmt.Sprintf("channel_store/%d/%s", nodeID, channel)) info := generateWatchInfo(channel, state) bs, err := proto.Marshal(info) s.Require().NoError(err) values = append(values, string(bs)) } s.mockKv.EXPECT().LoadWithPrefix(mock.Anything).Return(keys, values, nil).Once() } func (s *ChannelManagerSuite) checkAssignment(m *ChannelManagerImplV2, nodeID int64, channel string, state ChannelState) { rwChannel, found := m.GetChannel(nodeID, channel) s.True(found) s.NotNil(rwChannel) s.Equal(channel, rwChannel.GetName()) sChannel, ok := rwChannel.(*StateChannel) s.True(ok) s.Equal(state, sChannel.currentState) s.EqualValues(nodeID, sChannel.assignedNode) s.True(m.Match(nodeID, channel)) if nodeID != bufferID { gotNode, err := m.FindWatcher(channel) s.NoError(err) s.EqualValues(gotNode, nodeID) } } func (s *ChannelManagerSuite) checkNoAssignment(m *ChannelManagerImplV2, nodeID int64, channel string) { rwChannel, found := m.GetChannel(nodeID, channel) s.False(found) s.Nil(rwChannel) s.False(m.Match(nodeID, channel)) } func (s *ChannelManagerSuite) SetupTest() { s.mockKv = kvmock.NewMetaKv(s.T()) s.mockCluster = NewMockSubCluster(s.T()) s.mockAlloc = NewNMockAllocator(s.T()) s.mockHandler = NewNMockHandler(s.T()) s.mockHandler.EXPECT().GetDataVChanPositions(mock.Anything, mock.Anything). RunAndReturn(func(ch RWChannel, partitionID UniqueID) *datapb.VchannelInfo { return &datapb.VchannelInfo{ CollectionID: ch.GetCollectionID(), ChannelName: ch.GetName(), } }).Maybe() s.mockAlloc.EXPECT().allocID(mock.Anything).Return(19530, nil).Maybe() s.mockKv.EXPECT().MultiSaveAndRemove(mock.Anything, mock.Anything).RunAndReturn( func(save map[string]string, removals []string, preds ...predicates.Predicate) error { log.Info("test save and remove", zap.Any("save", save), zap.Any("removals", removals)) return nil }).Maybe() } func (s *ChannelManagerSuite) TearDownTest() {} func (s *ChannelManagerSuite) TestAddNode() { s.Run("AddNode with empty store", func() { s.prepareMeta(nil, 0) m, err := NewChannelManagerV2(s.mockKv, s.mockHandler, s.mockCluster, s.mockAlloc) s.Require().NoError(err) var testNode int64 = 1 err = m.AddNode(testNode) s.NoError(err) info := m.store.GetNode(testNode) s.NotNil(info) s.Empty(info.Channels) s.Equal(info.NodeID, testNode) }) s.Run("AddNode with channel in bufferID", func() { chNodes := map[string]int64{ "ch1": bufferID, "ch2": bufferID, } s.prepareMeta(chNodes, datapb.ChannelWatchState_ToWatch) m, err := NewChannelManagerV2(s.mockKv, s.mockHandler, s.mockCluster, s.mockAlloc) s.Require().NoError(err) var ( testNodeID int64 = 1 testChannels = []string{"ch1", "ch2"} ) lo.ForEach(testChannels, func(ch string, _ int) { s.checkAssignment(m, bufferID, ch, Standby) }) err = m.AddNode(testNodeID) s.NoError(err) lo.ForEach(testChannels, func(ch string, _ int) { s.checkAssignment(m, testNodeID, ch, ToWatch) }) }) s.Run("AddNode with channels evenly in other node", func() { var ( testNodeID int64 = 100 storedNodeID int64 = 1 testChannel = "ch1" ) chNodes := map[string]int64{testChannel: storedNodeID} s.prepareMeta(chNodes, datapb.ChannelWatchState_WatchSuccess) m, err := NewChannelManagerV2(s.mockKv, s.mockHandler, s.mockCluster, s.mockAlloc) s.Require().NoError(err) s.checkAssignment(m, storedNodeID, testChannel, Watched) err = m.AddNode(testNodeID) s.NoError(err) s.ElementsMatch([]int64{100, 1}, m.store.GetNodes()) s.checkNoAssignment(m, testNodeID, testChannel) testNodeID = 101 paramtable.Get().Save(paramtable.Get().DataCoordCfg.AutoBalance.Key, "true") defer paramtable.Get().Reset(paramtable.Get().DataCoordCfg.AutoBalance.Key) err = m.AddNode(testNodeID) s.NoError(err) s.ElementsMatch([]int64{100, 101, 1}, m.store.GetNodes()) s.checkNoAssignment(m, testNodeID, testChannel) }) s.Run("AddNode with channels unevenly in other node", func() { chNodes := map[string]int64{ "ch1": 1, "ch2": 1, "ch3": 1, } s.prepareMeta(chNodes, datapb.ChannelWatchState_WatchSuccess) m, err := NewChannelManagerV2(s.mockKv, s.mockHandler, s.mockCluster, s.mockAlloc) s.Require().NoError(err) var testNodeID int64 = 100 paramtable.Get().Save(paramtable.Get().DataCoordCfg.AutoBalance.Key, "true") defer paramtable.Get().Reset(paramtable.Get().DataCoordCfg.AutoBalance.Key) err = m.AddNode(testNodeID) s.NoError(err) s.ElementsMatch([]int64{testNodeID, 1}, m.store.GetNodes()) }) } func (s *ChannelManagerSuite) TestWatch() { s.Run("test Watch with empty store", func() { s.prepareMeta(nil, 0) m, err := NewChannelManagerV2(s.mockKv, s.mockHandler, s.mockCluster, s.mockAlloc) s.Require().NoError(err) var testCh string = "ch1" err = m.Watch(context.TODO(), getChannel(testCh, 1)) s.NoError(err) s.checkAssignment(m, bufferID, testCh, Standby) }) s.Run("test Watch with nodeID in store", func() { s.prepareMeta(nil, 0) m, err := NewChannelManagerV2(s.mockKv, s.mockHandler, s.mockCluster, s.mockAlloc) s.Require().NoError(err) var ( testCh string = "ch1" testNodeID int64 = 1 ) err = m.AddNode(testNodeID) s.NoError(err) s.checkNoAssignment(m, testNodeID, testCh) err = m.Watch(context.TODO(), getChannel(testCh, 1)) s.NoError(err) s.checkAssignment(m, testNodeID, testCh, ToWatch) }) } func (s *ChannelManagerSuite) TestRelease() { s.Run("release not exist nodeID and channel", func() { s.prepareMeta(nil, 0) m, err := NewChannelManagerV2(s.mockKv, s.mockHandler, s.mockCluster, s.mockAlloc) s.Require().NoError(err) err = m.Release(1, "ch1") s.Error(err) log.Info("error", zap.String("msg", err.Error())) m.AddNode(1) err = m.Release(1, "ch1") s.Error(err) log.Info("error", zap.String("msg", err.Error())) }) s.Run("release channel in bufferID", func() { s.prepareMeta(nil, 0) m, err := NewChannelManagerV2(s.mockKv, s.mockHandler, s.mockCluster, s.mockAlloc) s.Require().NoError(err) m.Watch(context.TODO(), getChannel("ch1", 1)) s.checkAssignment(m, bufferID, "ch1", Standby) err = m.Release(bufferID, "ch1") s.NoError(err) s.checkAssignment(m, bufferID, "ch1", Standby) }) } func (s *ChannelManagerSuite) TestDeleteNode() { s.Run("delete not exsit node", func() { s.prepareMeta(nil, 0) m, err := NewChannelManagerV2(s.mockKv, s.mockHandler, s.mockCluster, s.mockAlloc) s.Require().NoError(err) info := m.store.GetNode(1) s.Require().Nil(info) err = m.DeleteNode(1) s.NoError(err) }) s.Run("delete bufferID", func() { s.prepareMeta(nil, 0) m, err := NewChannelManagerV2(s.mockKv, s.mockHandler, s.mockCluster, s.mockAlloc) s.Require().NoError(err) info := m.store.GetNode(bufferID) s.Require().NotNil(info) err = m.DeleteNode(bufferID) s.NoError(err) }) s.Run("delete node without assigment", func() { s.prepareMeta(nil, 0) m, err := NewChannelManagerV2(s.mockKv, s.mockHandler, s.mockCluster, s.mockAlloc) s.Require().NoError(err) err = m.AddNode(1) s.NoError(err) info := m.store.GetNode(bufferID) s.Require().NotNil(info) err = m.DeleteNode(1) s.NoError(err) info = m.store.GetNode(1) s.Nil(info) }) s.Run("delete node with channel", func() { chNodes := map[string]int64{ "ch1": 1, "ch2": 1, "ch3": 1, } s.prepareMeta(chNodes, datapb.ChannelWatchState_WatchSuccess) m, err := NewChannelManagerV2(s.mockKv, s.mockHandler, s.mockCluster, s.mockAlloc) s.Require().NoError(err) s.checkAssignment(m, 1, "ch1", Watched) s.checkAssignment(m, 1, "ch2", Watched) s.checkAssignment(m, 1, "ch3", Watched) err = m.AddNode(2) s.NoError(err) err = m.DeleteNode(1) s.NoError(err) info := m.store.GetNode(bufferID) s.NotNil(info) s.Equal(3, len(info.Channels)) s.EqualValues(bufferID, info.NodeID) s.checkAssignment(m, bufferID, "ch1", Standby) s.checkAssignment(m, bufferID, "ch2", Standby) s.checkAssignment(m, bufferID, "ch3", Standby) info = m.store.GetNode(1) s.Nil(info) }) } func (s *ChannelManagerSuite) TestFindWatcher() { chNodes := map[string]int64{ "ch1": bufferID, "ch2": bufferID, "ch3": 1, "ch4": 1, } s.prepareMeta(chNodes, datapb.ChannelWatchState_WatchSuccess) m, err := NewChannelManagerV2(s.mockKv, s.mockHandler, s.mockCluster, s.mockAlloc) s.Require().NoError(err) tests := []struct { description string testCh string outNodeID int64 outError bool }{ {"channel not exist", "ch-notexist", 0, true}, {"channel in bufferID", "ch1", bufferID, true}, {"channel in bufferID", "ch2", bufferID, true}, {"channel in nodeID=1", "ch3", 1, false}, {"channel in nodeID=1", "ch4", 1, false}, } for _, test := range tests { s.Run(test.description, func() { gotID, gotErr := m.FindWatcher(test.testCh) s.EqualValues(test.outNodeID, gotID) if test.outError { s.Error(gotErr) } else { s.NoError(gotErr) } }) } } func (s *ChannelManagerSuite) TestAdvanceChannelState() { ctx, cancel := context.WithCancel(context.Background()) defer cancel() s.Run("advance statndby with no available nodes", func() { chNodes := map[string]int64{ "ch1": bufferID, "ch2": bufferID, } s.prepareMeta(chNodes, datapb.ChannelWatchState_ToWatch) s.mockHandler.EXPECT().CheckShouldDropChannel(mock.Anything).Return(false) m, err := NewChannelManagerV2(s.mockKv, s.mockHandler, s.mockCluster, s.mockAlloc) s.Require().NoError(err) s.checkAssignment(m, bufferID, "ch1", Standby) s.checkAssignment(m, bufferID, "ch2", Standby) m.AdvanceChannelState(ctx) s.checkAssignment(m, bufferID, "ch1", Standby) s.checkAssignment(m, bufferID, "ch2", Standby) }) s.Run("advance statndby with node 1", func() { chNodes := map[string]int64{ "ch1": bufferID, "ch2": bufferID, "ch3": 1, } s.prepareMeta(chNodes, datapb.ChannelWatchState_WatchSuccess) s.mockHandler.EXPECT().CheckShouldDropChannel(mock.Anything).Return(false).Times(2) m, err := NewChannelManagerV2(s.mockKv, s.mockHandler, s.mockCluster, s.mockAlloc) s.Require().NoError(err) s.checkAssignment(m, bufferID, "ch1", Standby) s.checkAssignment(m, bufferID, "ch2", Standby) s.checkAssignment(m, 1, "ch3", Watched) m.AdvanceChannelState(ctx) s.checkAssignment(m, 1, "ch1", ToWatch) s.checkAssignment(m, 1, "ch2", ToWatch) }) s.Run("advance towatch channels notify success check success", func() { chNodes := map[string]int64{ "ch1": 1, "ch2": 1, } s.prepareMeta(chNodes, datapb.ChannelWatchState_ToWatch) s.mockCluster.EXPECT().NotifyChannelOperation(mock.Anything, mock.Anything, mock.Anything).Return(nil).Twice() m, err := NewChannelManagerV2(s.mockKv, s.mockHandler, s.mockCluster, s.mockAlloc) s.Require().NoError(err) s.checkAssignment(m, 1, "ch1", ToWatch) s.checkAssignment(m, 1, "ch2", ToWatch) m.AdvanceChannelState(ctx) s.checkAssignment(m, 1, "ch1", Watching) s.checkAssignment(m, 1, "ch2", Watching) }) s.Run("advance watching channels check no progress", func() { chNodes := map[string]int64{ "ch1": 1, "ch2": 1, } s.prepareMeta(chNodes, datapb.ChannelWatchState_ToWatch) s.mockCluster.EXPECT().NotifyChannelOperation(mock.Anything, mock.Anything, mock.Anything).Return(nil).Twice() m, err := NewChannelManagerV2(s.mockKv, s.mockHandler, s.mockCluster, s.mockAlloc) s.Require().NoError(err) s.checkAssignment(m, 1, "ch1", ToWatch) s.checkAssignment(m, 1, "ch2", ToWatch) m.AdvanceChannelState(ctx) s.checkAssignment(m, 1, "ch1", Watching) s.checkAssignment(m, 1, "ch2", Watching) s.mockCluster.EXPECT().CheckChannelOperationProgress(mock.Anything, mock.Anything, mock.Anything). Return(&datapb.ChannelOperationProgressResponse{State: datapb.ChannelWatchState_ToWatch}, nil).Twice() m.AdvanceChannelState(ctx) s.checkAssignment(m, 1, "ch1", Watching) s.checkAssignment(m, 1, "ch2", Watching) }) s.Run("advance watching channels check watch success", func() { chNodes := map[string]int64{ "ch1": 1, "ch2": 1, } s.prepareMeta(chNodes, datapb.ChannelWatchState_ToWatch) s.mockCluster.EXPECT().NotifyChannelOperation(mock.Anything, mock.Anything, mock.Anything).Return(nil).Twice() m, err := NewChannelManagerV2(s.mockKv, s.mockHandler, s.mockCluster, s.mockAlloc) s.Require().NoError(err) s.checkAssignment(m, 1, "ch1", ToWatch) s.checkAssignment(m, 1, "ch2", ToWatch) m.AdvanceChannelState(ctx) s.checkAssignment(m, 1, "ch1", Watching) s.checkAssignment(m, 1, "ch2", Watching) s.mockCluster.EXPECT().CheckChannelOperationProgress(mock.Anything, mock.Anything, mock.Anything). Return(&datapb.ChannelOperationProgressResponse{State: datapb.ChannelWatchState_WatchSuccess}, nil).Twice() m.AdvanceChannelState(ctx) s.checkAssignment(m, 1, "ch1", Watched) s.checkAssignment(m, 1, "ch2", Watched) }) s.Run("advance watching channels check watch fail", func() { chNodes := map[string]int64{ "ch1": 1, "ch2": 1, } s.prepareMeta(chNodes, datapb.ChannelWatchState_ToWatch) s.mockCluster.EXPECT().NotifyChannelOperation(mock.Anything, mock.Anything, mock.Anything).Return(nil).Times(2) m, err := NewChannelManagerV2(s.mockKv, s.mockHandler, s.mockCluster, s.mockAlloc) s.Require().NoError(err) s.checkAssignment(m, 1, "ch1", ToWatch) s.checkAssignment(m, 1, "ch2", ToWatch) m.AdvanceChannelState(ctx) s.checkAssignment(m, 1, "ch1", Watching) s.checkAssignment(m, 1, "ch2", Watching) s.mockCluster.EXPECT().CheckChannelOperationProgress(mock.Anything, mock.Anything, mock.Anything). Return(&datapb.ChannelOperationProgressResponse{State: datapb.ChannelWatchState_WatchFailure}, nil).Twice() m.AdvanceChannelState(ctx) s.checkAssignment(m, 1, "ch1", Standby) s.checkAssignment(m, 1, "ch2", Standby) s.mockHandler.EXPECT().CheckShouldDropChannel(mock.Anything).Return(false) m.AdvanceChannelState(ctx) s.checkAssignment(m, 1, "ch1", ToWatch) s.checkAssignment(m, 1, "ch2", ToWatch) }) s.Run("advance releasing channels check release no progress", func() { chNodes := map[string]int64{ "ch1": 1, "ch2": 1, } s.prepareMeta(chNodes, datapb.ChannelWatchState_ToRelease) s.mockCluster.EXPECT().NotifyChannelOperation(mock.Anything, mock.Anything, mock.Anything).Return(nil).Twice() m, err := NewChannelManagerV2(s.mockKv, s.mockHandler, s.mockCluster, s.mockAlloc) s.Require().NoError(err) s.checkAssignment(m, 1, "ch1", ToRelease) s.checkAssignment(m, 1, "ch2", ToRelease) m.AdvanceChannelState(ctx) s.checkAssignment(m, 1, "ch1", Releasing) s.checkAssignment(m, 1, "ch2", Releasing) s.mockCluster.EXPECT().CheckChannelOperationProgress(mock.Anything, mock.Anything, mock.Anything). Return(&datapb.ChannelOperationProgressResponse{State: datapb.ChannelWatchState_ToRelease}, nil).Twice() m.AdvanceChannelState(ctx) s.checkAssignment(m, 1, "ch1", Releasing) s.checkAssignment(m, 1, "ch2", Releasing) }) s.Run("advance releasing channels check release success", func() { chNodes := map[string]int64{ "ch1": 1, "ch2": 1, } s.prepareMeta(chNodes, datapb.ChannelWatchState_ToRelease) s.mockCluster.EXPECT().NotifyChannelOperation(mock.Anything, mock.Anything, mock.Anything).Return(nil).Twice() m, err := NewChannelManagerV2(s.mockKv, s.mockHandler, s.mockCluster, s.mockAlloc) s.Require().NoError(err) s.checkAssignment(m, 1, "ch1", ToRelease) s.checkAssignment(m, 1, "ch2", ToRelease) m.AdvanceChannelState(ctx) s.checkAssignment(m, 1, "ch1", Releasing) s.checkAssignment(m, 1, "ch2", Releasing) s.mockCluster.EXPECT().CheckChannelOperationProgress(mock.Anything, mock.Anything, mock.Anything). Return(&datapb.ChannelOperationProgressResponse{State: datapb.ChannelWatchState_ReleaseSuccess}, nil).Twice() m.AdvanceChannelState(ctx) s.checkAssignment(m, 1, "ch1", Standby) s.checkAssignment(m, 1, "ch2", Standby) s.mockHandler.EXPECT().CheckShouldDropChannel(mock.Anything).Return(false) m.AdvanceChannelState(ctx) s.checkAssignment(m, 1, "ch1", ToWatch) s.checkAssignment(m, 1, "ch2", ToWatch) }) s.Run("advance releasing channels check release fail", func() { chNodes := map[string]int64{ "ch1": 1, "ch2": 1, } s.prepareMeta(chNodes, datapb.ChannelWatchState_ToRelease) s.mockCluster.EXPECT().NotifyChannelOperation(mock.Anything, mock.Anything, mock.Anything).Return(nil).Twice() m, err := NewChannelManagerV2(s.mockKv, s.mockHandler, s.mockCluster, s.mockAlloc) s.Require().NoError(err) s.checkAssignment(m, 1, "ch1", ToRelease) s.checkAssignment(m, 1, "ch2", ToRelease) m.AdvanceChannelState(ctx) s.checkAssignment(m, 1, "ch1", Releasing) s.checkAssignment(m, 1, "ch2", Releasing) s.mockCluster.EXPECT().CheckChannelOperationProgress(mock.Anything, mock.Anything, mock.Anything). Return(&datapb.ChannelOperationProgressResponse{State: datapb.ChannelWatchState_ReleaseFailure}, nil).Twice() m.AdvanceChannelState(ctx) s.checkAssignment(m, 1, "ch1", Standby) s.checkAssignment(m, 1, "ch2", Standby) s.mockHandler.EXPECT().CheckShouldDropChannel(mock.Anything).Return(false) m.AdvanceChannelState(ctx) // TODO, donot assign to abnormal nodes s.checkAssignment(m, 1, "ch1", ToWatch) s.checkAssignment(m, 1, "ch2", ToWatch) }) s.Run("advance towatch channels notify fail", func() { chNodes := map[string]int64{ "ch1": 1, "ch2": 1, } s.prepareMeta(chNodes, datapb.ChannelWatchState_ToWatch) s.mockCluster.EXPECT().NotifyChannelOperation(mock.Anything, mock.Anything, mock.Anything). Return(fmt.Errorf("mock error")).Twice() m, err := NewChannelManagerV2(s.mockKv, s.mockHandler, s.mockCluster, s.mockAlloc) s.Require().NoError(err) s.checkAssignment(m, 1, "ch1", ToWatch) s.checkAssignment(m, 1, "ch2", ToWatch) m.AdvanceChannelState(ctx) s.checkAssignment(m, 1, "ch1", ToWatch) s.checkAssignment(m, 1, "ch2", ToWatch) }) s.Run("advance to release channels notify success", func() { chNodes := map[string]int64{ "ch1": 1, "ch2": 1, } s.prepareMeta(chNodes, datapb.ChannelWatchState_ToRelease) s.mockCluster.EXPECT().NotifyChannelOperation(mock.Anything, mock.Anything, mock.Anything).Return(nil).Twice() m, err := NewChannelManagerV2(s.mockKv, s.mockHandler, s.mockCluster, s.mockAlloc) s.Require().NoError(err) s.checkAssignment(m, 1, "ch1", ToRelease) s.checkAssignment(m, 1, "ch2", ToRelease) m.AdvanceChannelState(ctx) s.checkAssignment(m, 1, "ch1", Releasing) s.checkAssignment(m, 1, "ch2", Releasing) }) s.Run("advance to release channels notify fail", func() { chNodes := map[string]int64{ "ch1": 1, "ch2": 1, } s.prepareMeta(chNodes, datapb.ChannelWatchState_ToRelease) s.mockCluster.EXPECT().NotifyChannelOperation(mock.Anything, mock.Anything, mock.Anything). Return(fmt.Errorf("mock error")).Twice() m, err := NewChannelManagerV2(s.mockKv, s.mockHandler, s.mockCluster, s.mockAlloc) s.Require().NoError(err) s.checkAssignment(m, 1, "ch1", ToRelease) s.checkAssignment(m, 1, "ch2", ToRelease) m.AdvanceChannelState(ctx) s.checkAssignment(m, 1, "ch1", ToRelease) s.checkAssignment(m, 1, "ch2", ToRelease) }) } func (s *ChannelManagerSuite) TestStartup() { chNodes := map[string]int64{ "ch1": 1, "ch2": 1, "ch3": 3, } s.prepareMeta(chNodes, datapb.ChannelWatchState_ToRelease) s.mockHandler.EXPECT().CheckShouldDropChannel(mock.Anything).Return(false) m, err := NewChannelManagerV2(s.mockKv, s.mockHandler, s.mockCluster, s.mockAlloc) s.Require().NoError(err) var ( legacyNodes = []int64{1} allNodes = []int64{1} ) err = m.Startup(context.TODO(), legacyNodes, allNodes) s.NoError(err) s.checkAssignment(m, 1, "ch1", Legacy) s.checkAssignment(m, 1, "ch2", Legacy) s.checkAssignment(m, bufferID, "ch3", Standby) err = m.DeleteNode(1) s.NoError(err) s.checkAssignment(m, bufferID, "ch1", Standby) s.checkAssignment(m, bufferID, "ch2", Standby) err = m.AddNode(2) s.NoError(err) s.checkAssignment(m, 2, "ch1", ToWatch) s.checkAssignment(m, 2, "ch2", ToWatch) s.checkAssignment(m, 2, "ch3", ToWatch) } func (s *ChannelManagerSuite) TestCheckLoop() {} func (s *ChannelManagerSuite) TestGet() {}