github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/engine/internal/pkg/discovery/agent_test.go (about) 1 // Copyright 2022 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package discovery 15 16 import ( 17 "context" 18 "reflect" 19 "sync" 20 "testing" 21 "time" 22 23 "github.com/golang/mock/gomock" 24 pb "github.com/pingcap/tiflow/engine/enginepb" 25 pkgClient "github.com/pingcap/tiflow/engine/pkg/client" 26 "github.com/stretchr/testify/require" 27 ) 28 29 func TestAgent(t *testing.T) { 30 t.Parallel() 31 32 discoveryClient := pkgClient.NewMockServerMasterClient(gomock.NewController(t)) 33 agent := NewAgent(discoveryClient, time.Millisecond*100) 34 35 var nodes sync.Map 36 37 discoveryClient.EXPECT(). 38 ListMasters(gomock.Any()).AnyTimes(). 39 DoAndReturn(func(ctx context.Context) ([]*pb.Master, error) { 40 var ret []*pb.Master 41 nodes.Range(func(_, value any) bool { 42 node := value.(Node) 43 if node.Tp == NodeTypeMaster { 44 ret = append(ret, &pb.Master{ 45 Id: node.ID, 46 Address: node.Addr, 47 }) 48 } 49 return true 50 }) 51 return ret, nil 52 }) 53 54 discoveryClient.EXPECT(). 55 ListExecutors(gomock.Any()).AnyTimes(). 56 DoAndReturn(func(ctx context.Context) ([]*pb.Executor, error) { 57 var ret []*pb.Executor 58 nodes.Range(func(_, value any) bool { 59 node := value.(Node) 60 if node.Tp == NodeTypeExecutor { 61 ret = append(ret, &pb.Executor{ 62 Id: node.ID, 63 Address: node.Addr, 64 }) 65 } 66 return true 67 }) 68 return ret, nil 69 }) 70 71 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 72 defer cancel() 73 74 var wg sync.WaitGroup 75 wg.Add(1) 76 go func() { 77 defer wg.Done() 78 79 err := agent.Run(ctx) 80 require.ErrorIs(t, err, context.Canceled) 81 }() 82 83 initialSnap := Snapshot{ 84 "master-1": Node{ 85 Tp: NodeTypeMaster, 86 ID: "master-1", 87 Addr: "dummy-1:2222", 88 }, 89 "executor-2": Node{ 90 Tp: NodeTypeExecutor, 91 ID: "executor-2", 92 Addr: "dummy-2:2222", 93 }, 94 "executor-3": Node{ 95 Tp: NodeTypeExecutor, 96 ID: "executor-3", 97 Addr: "dummy-3:2222", 98 }, 99 } 100 101 for _, node := range initialSnap { 102 nodes.Store(node.ID, node) 103 } 104 105 require.Eventually(t, func() bool { 106 snap, receiver, err := agent.Subscribe(ctx) 107 require.NoError(t, err) 108 receiver.Close() 109 return reflect.DeepEqual(snap, initialSnap) 110 }, time.Second, time.Millisecond*100, "should get initial snapshot") 111 112 _, receiver, err := agent.Subscribe(ctx) 113 require.NoError(t, err) 114 115 expectedEvents := []Event{ 116 { 117 Tp: EventTypeAdd, 118 Node: Node{ 119 Tp: NodeTypeExecutor, 120 ID: "executor-4", 121 Addr: "dummy-4:2222", 122 }, 123 }, 124 { 125 Tp: EventTypeAdd, 126 Node: Node{ 127 Tp: NodeTypeExecutor, 128 ID: "executor-5", 129 Addr: "dummy-5:2222", 130 }, 131 }, 132 { 133 Tp: EventTypeDel, 134 Node: Node{ 135 Tp: NodeTypeExecutor, 136 ID: "executor-3", 137 Addr: "dummy-3:2222", 138 }, 139 }, 140 { 141 Tp: EventTypeAdd, 142 Node: Node{ 143 Tp: NodeTypeMaster, 144 ID: "master-6", 145 Addr: "dummy-6:2222", 146 }, 147 }, 148 { 149 Tp: EventTypeDel, 150 Node: Node{ 151 Tp: NodeTypeMaster, 152 ID: "master-1", 153 Addr: "dummy-1:2222", 154 }, 155 }, 156 } 157 158 for _, expectedEvent := range expectedEvents { 159 if expectedEvent.Tp == EventTypeAdd { 160 nodes.Store(expectedEvent.Node.ID, expectedEvent.Node) 161 } else { 162 nodes.Delete(expectedEvent.Node.ID) 163 } 164 select { 165 case <-ctx.Done(): 166 require.Failf(t, "unreachable", "%s", ctx.Err().Error()) 167 case event := <-receiver.C: 168 require.Equal(t, expectedEvent, event) 169 } 170 } 171 receiver.Close() 172 173 require.Eventually(t, func() bool { 174 snap, receiver, err := agent.Subscribe(ctx) 175 require.NoError(t, err) 176 receiver.Close() 177 return reflect.DeepEqual(snap, Snapshot{ 178 "executor-2": Node{ 179 Tp: NodeTypeExecutor, 180 ID: "executor-2", 181 Addr: "dummy-2:2222", 182 }, 183 "executor-4": Node{ 184 Tp: NodeTypeExecutor, 185 ID: "executor-4", 186 Addr: "dummy-4:2222", 187 }, 188 "executor-5": Node{ 189 Tp: NodeTypeExecutor, 190 ID: "executor-5", 191 Addr: "dummy-5:2222", 192 }, 193 "master-6": Node{ 194 Tp: NodeTypeMaster, 195 ID: "master-6", 196 Addr: "dummy-6:2222", 197 }, 198 }) 199 }, time.Second, time.Millisecond*100, "should get expected snapshot") 200 201 cancel() 202 wg.Wait() 203 }