github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/pkg/p2p/message_router_test.go (about) 1 // Copyright 2021 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 p2p 15 16 import ( 17 "context" 18 "fmt" 19 "net" 20 "strings" 21 "sync" 22 "testing" 23 "time" 24 25 "github.com/pingcap/failpoint" 26 "github.com/pingcap/tiflow/pkg/security" 27 "github.com/pingcap/tiflow/proto/p2p" 28 "github.com/stretchr/testify/require" 29 "github.com/tikv/pd/pkg/utils/tempurl" 30 "google.golang.org/grpc" 31 ) 32 33 type messageRouterTestSuite struct { 34 servers map[NodeID]*MessageServer 35 cancels map[NodeID]context.CancelFunc 36 messageRouter *messageRouterImpl 37 wg sync.WaitGroup 38 } 39 40 // read only 41 var clientConfig4TestingMessageRouter = &MessageClientConfig{ 42 SendChannelSize: 128, 43 BatchSendInterval: time.Millisecond * 200, 44 MaxBatchCount: 128, 45 MaxBatchBytes: 8192, 46 RetryRateLimitPerSecond: 10.0, // using 10.0 instead of 1.0 to accelerate testing 47 DialTimeout: time.Second * 3, 48 MaxRecvMsgSize: 4 * 1024 * 1024, // 4MB 49 } 50 51 func newMessageRouterTestSuite() *messageRouterTestSuite { 52 return &messageRouterTestSuite{ 53 servers: map[NodeID]*MessageServer{}, 54 cancels: map[NodeID]context.CancelFunc{}, 55 messageRouter: NewMessageRouterWithLocalClient( 56 "test-client-1", 57 &security.Credential{}, 58 clientConfig4TestingMessageRouter), 59 } 60 } 61 62 func (s *messageRouterTestSuite) getServer(id NodeID) *MessageServer { 63 return s.servers[id] 64 } 65 66 func (s *messageRouterTestSuite) addServer(ctx context.Context, t *testing.T, id NodeID) { 67 addr := strings.TrimPrefix(tempurl.Alloc(), "http://") 68 lis, err := net.Listen("tcp", addr) 69 require.NoError(t, err) 70 71 grpcServer := grpc.NewServer() 72 newServer := NewMessageServer(id, defaultServerConfig4Testing) 73 p2p.RegisterCDCPeerToPeerServer(grpcServer, newServer) 74 75 ctx, cancel := context.WithCancel(ctx) 76 s.cancels[id] = cancel 77 s.servers[id] = newServer 78 79 s.messageRouter.AddPeer(id, addr) 80 81 s.wg.Add(1) 82 go func() { 83 defer s.wg.Done() 84 _ = grpcServer.Serve(lis) 85 }() 86 87 s.wg.Add(1) 88 go func() { 89 defer s.wg.Done() 90 defer grpcServer.Stop() 91 defer s.messageRouter.RemovePeer(id) 92 err := newServer.Run(ctx, nil) 93 if err != nil { 94 require.Regexp(t, ".*context canceled.*", err.Error()) 95 } 96 }() 97 } 98 99 func (s *messageRouterTestSuite) close() { 100 for _, cancel := range s.cancels { 101 cancel() 102 } 103 s.wg.Wait() 104 105 s.messageRouter.Close() 106 } 107 108 func TestMessageRouterBasic(t *testing.T) { 109 ctx, cancel := context.WithTimeout(context.TODO(), defaultTimeout) 110 defer cancel() 111 112 suite := newMessageRouterTestSuite() 113 suite.addServer(ctx, t, "server-1") 114 suite.addServer(ctx, t, "server-2") 115 suite.addServer(ctx, t, "server-3") 116 117 selfID := suite.messageRouter.selfID 118 localClient := suite.messageRouter.GetClient(selfID) 119 require.NotNil(t, localClient) 120 require.NotNil(t, suite.messageRouter.GetLocalChannel()) 121 122 noClient := suite.messageRouter.GetClient("server-4") 123 require.Nilf(t, noClient, "no client should have been created") 124 125 var lastIndex [3]int64 126 mustAddHandler(ctx, t, suite.getServer("server-1"), "test-topic", &testTopicContent{}, func(senderID string, i interface{}) error { 127 require.Equal(t, "test-client-1", senderID) 128 require.IsType(t, &testTopicContent{}, i) 129 content := i.(*testTopicContent) 130 require.Equal(t, content.Index, lastIndex[0]+1) 131 lastIndex[0] = content.Index 132 return nil 133 }) 134 135 mustAddHandler(ctx, t, suite.getServer("server-2"), "test-topic", &testTopicContent{}, func(senderID string, i interface{}) error { 136 require.Equal(t, "test-client-1", senderID) 137 require.IsType(t, &testTopicContent{}, i) 138 content := i.(*testTopicContent) 139 require.Equal(t, content.Index, lastIndex[1]+1) 140 lastIndex[1] = content.Index 141 return nil 142 }) 143 144 mustAddHandler(ctx, t, suite.getServer("server-3"), "test-topic", &testTopicContent{}, func(senderID string, i interface{}) error { 145 require.Equal(t, "test-client-1", senderID) 146 require.IsType(t, &testTopicContent{}, i) 147 content := i.(*testTopicContent) 148 require.Equal(t, content.Index, lastIndex[2]+1) 149 lastIndex[2] = content.Index 150 return nil 151 }) 152 153 var lastSeq [3]Seq 154 for i := 0; i < defaultMessageBatchSizeLarge; i++ { 155 serverIdx := i % 3 156 serverID := fmt.Sprintf("server-%d", serverIdx+1) 157 Seq, err := suite.messageRouter.GetClient(serverID).SendMessage(ctx, "test-topic", &testTopicContent{int64(i/3) + 1}) 158 require.NoError(t, err) 159 lastSeq[serverIdx] = Seq 160 } 161 162 require.Eventually(t, func() bool { 163 seq, ok := suite.messageRouter.GetClient("server-1").CurrentAck("test-topic") 164 if !ok { 165 return false 166 } 167 return seq >= lastSeq[0] 168 }, time.Second*10, time.Millisecond*20) 169 170 require.Eventually(t, func() bool { 171 seq, ok := suite.messageRouter.GetClient("server-2").CurrentAck("test-topic") 172 if !ok { 173 return false 174 } 175 return seq >= lastSeq[1] 176 }, time.Second*10, time.Millisecond*20) 177 178 require.Eventually(t, func() bool { 179 seq, ok := suite.messageRouter.GetClient("server-3").CurrentAck("test-topic") 180 if !ok { 181 return false 182 } 183 return seq >= lastSeq[2] 184 }, time.Second*10, time.Millisecond*20) 185 186 suite.close() 187 suite.close() // double close: should not panic 188 suite.close() // triple close: should not panic 189 } 190 191 func TestMessageRouterRemovePeer(t *testing.T) { 192 ctx, cancel := context.WithTimeout(context.TODO(), defaultTimeout) 193 defer cancel() 194 195 suite := newMessageRouterTestSuite() 196 suite.addServer(ctx, t, "server-1") 197 suite.addServer(ctx, t, "server-2") 198 199 var lastIndex [3]int64 200 mustAddHandler(ctx, t, suite.getServer("server-1"), "test-topic", &testTopicContent{}, func(senderID string, i interface{}) error { 201 require.Equal(t, "test-client-1", senderID) 202 require.IsType(t, &testTopicContent{}, i) 203 content := i.(*testTopicContent) 204 require.Equal(t, content.Index, lastIndex[0]+1) 205 lastIndex[0] = content.Index 206 return nil 207 }) 208 209 mustAddHandler(ctx, t, suite.getServer("server-2"), "test-topic", &testTopicContent{}, func(senderID string, i interface{}) error { 210 require.Equal(t, "test-client-1", senderID) 211 require.IsType(t, &testTopicContent{}, i) 212 content := i.(*testTopicContent) 213 require.Equal(t, content.Index, lastIndex[1]+1) 214 lastIndex[1] = content.Index 215 return nil 216 }) 217 218 var wg sync.WaitGroup 219 wg.Add(1) 220 go func() { 221 defer wg.Done() 222 var lastSeq Seq 223 for i := 0; i < defaultMessageBatchSizeLarge; i++ { 224 var err error 225 lastSeq, err = suite.messageRouter.GetClient("server-1"). 226 SendMessage(ctx, "test-topic", &testTopicContent{int64(i + 1)}) 227 require.NoError(t, err) 228 } 229 require.Eventually(t, func() bool { 230 seq, ok := suite.messageRouter.GetClient("server-1").CurrentAck("test-topic") 231 if !ok { 232 return false 233 } 234 return seq >= lastSeq 235 }, time.Second*10, time.Millisecond*20) 236 }() 237 238 wg.Add(1) 239 go func() { 240 defer wg.Done() 241 client := suite.messageRouter.GetClient("server-2") 242 require.NotNil(t, client) 243 for i := 0; i < defaultMessageBatchSizeSmall; i++ { 244 var err error 245 _, err = client.SendMessage(ctx, "test-topic", &testTopicContent{int64(i + 1)}) 246 require.NoError(t, err) 247 } 248 suite.messageRouter.RemovePeer("server-2") 249 250 var err error 251 require.Eventually(t, func() bool { 252 _, err = client.SendMessage(ctx, "test-topic", &testTopicContent{0}) 253 return err != nil 254 }, time.Millisecond*500, time.Millisecond*50) 255 require.Regexp(t, ".*ErrPeerMessageClientClosed.*", err.Error()) 256 }() 257 258 wg.Wait() 259 suite.close() 260 } 261 262 func TestMessageRouterClientFailure(t *testing.T) { 263 ctx, cancel := context.WithTimeout(context.TODO(), defaultTimeout) 264 defer cancel() 265 266 suite := newMessageRouterTestSuite() 267 suite.addServer(ctx, t, "server-1") 268 269 // FIXME remove the failpoint test, and use a mock client instead 270 // But we should make MessageClient an interface first. 271 err := failpoint.Enable("github.com/pingcap/tiflow/pkg/p2p/InjectClientPermanentFailure", "return(true)") 272 require.NoError(t, err) 273 defer func() { 274 _ = failpoint.Disable("github.com/pingcap/tiflow/pkg/p2p/InjectClientPermanentFailure") 275 }() 276 277 client := suite.messageRouter.GetClient("server-1") 278 require.NotNil(t, client) 279 280 select { 281 case <-ctx.Done(): 282 require.Fail(t, "TestMessageRouterClientFailure timed out") 283 case err := <-suite.messageRouter.Err(): 284 require.NotNil(t, err) 285 require.Regexp(t, ".*ErrPeerMessageClientPermanentFail.*", err.Error()) 286 } 287 288 suite.close() 289 }