github.com/matrixorigin/matrixone@v0.7.0/pkg/logservice/hakeeper_client_test.go (about) 1 // Copyright 2021 - 2022 Matrix Origin 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 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package logservice 16 17 import ( 18 "context" 19 "sync" 20 "testing" 21 "time" 22 23 "github.com/google/uuid" 24 "github.com/lni/dragonboat/v4" 25 "github.com/lni/goutils/leaktest" 26 "github.com/lni/vfs" 27 "github.com/stretchr/testify/assert" 28 "github.com/stretchr/testify/require" 29 30 "github.com/matrixorigin/matrixone/pkg/common/moerr" 31 "github.com/matrixorigin/matrixone/pkg/common/morpc" 32 "github.com/matrixorigin/matrixone/pkg/hakeeper" 33 pb "github.com/matrixorigin/matrixone/pkg/pb/logservice" 34 "github.com/matrixorigin/matrixone/pkg/testutil" 35 ) 36 37 func TestHAKeeperClientConfigIsValidated(t *testing.T) { 38 cfg := HAKeeperClientConfig{} 39 cc1, err := NewCNHAKeeperClient(context.TODO(), cfg) 40 assert.Nil(t, cc1) 41 assert.True(t, moerr.IsMoErrCode(err, moerr.ErrBadConfig)) 42 cc2, err := NewDNHAKeeperClient(context.TODO(), cfg) 43 assert.Nil(t, cc2) 44 assert.True(t, moerr.IsMoErrCode(err, moerr.ErrBadConfig)) 45 cc3, err := NewLogHAKeeperClient(context.TODO(), cfg) 46 assert.Nil(t, cc3) 47 assert.True(t, moerr.IsMoErrCode(err, moerr.ErrBadConfig)) 48 } 49 50 func TestHAKeeperClientsCanBeCreated(t *testing.T) { 51 fn := func(t *testing.T, s *Service) { 52 cfg := HAKeeperClientConfig{ 53 ServiceAddresses: []string{testServiceAddress}, 54 } 55 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 56 defer cancel() 57 c1, err := NewCNHAKeeperClient(ctx, cfg) 58 require.NoError(t, err) 59 assert.NoError(t, c1.Close()) 60 c2, err := NewDNHAKeeperClient(ctx, cfg) 61 assert.NoError(t, err) 62 assert.NoError(t, c2.Close()) 63 c3, err := NewLogHAKeeperClient(ctx, cfg) 64 assert.NoError(t, err) 65 assert.NoError(t, c3.Close()) 66 } 67 runServiceTest(t, true, true, fn) 68 } 69 70 func TestHAKeeperClientCanNotConnectToNonHAKeeperNode(t *testing.T) { 71 fn := func(t *testing.T, s *Service) { 72 cfg := HAKeeperClientConfig{ 73 ServiceAddresses: []string{testServiceAddress}, 74 } 75 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 76 defer cancel() 77 _, err := NewCNHAKeeperClient(ctx, cfg) 78 require.True(t, moerr.IsMoErrCode(err, moerr.ErrNoHAKeeper)) 79 _, err = NewDNHAKeeperClient(ctx, cfg) 80 require.True(t, moerr.IsMoErrCode(err, moerr.ErrNoHAKeeper)) 81 _, err = NewLogHAKeeperClient(ctx, cfg) 82 require.True(t, moerr.IsMoErrCode(err, moerr.ErrNoHAKeeper)) 83 } 84 runServiceTest(t, false, true, fn) 85 } 86 87 func TestHAKeeperClientConnectByReverseProxy(t *testing.T) { 88 fn := func(t *testing.T, s *Service) { 89 done := false 90 for i := 0; i < 1000; i++ { 91 si, ok, err := GetShardInfo(testServiceAddress, hakeeper.DefaultHAKeeperShardID) 92 if err != nil || !ok { 93 time.Sleep(10 * time.Millisecond) 94 continue 95 } 96 done = true 97 require.NoError(t, err) 98 assert.True(t, ok) 99 assert.Equal(t, uint64(1), si.ReplicaID) 100 addr, ok := si.Replicas[si.ReplicaID] 101 assert.True(t, ok) 102 assert.Equal(t, testServiceAddress, addr) 103 break 104 } 105 if !done { 106 t.Fatalf("failed to get shard info") 107 } 108 // now shard info can be queried 109 cfg := HAKeeperClientConfig{ 110 ServiceAddresses: []string{"localhost:53033"}, // obvious not reachable 111 DiscoveryAddress: testServiceAddress, 112 } 113 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) 114 defer cancel() 115 c, err := NewLogHAKeeperClient(ctx, cfg) 116 require.NoError(t, err) 117 defer func() { 118 assert.NoError(t, c.Close()) 119 }() 120 121 hb := s.store.getHeartbeatMessage() 122 cb, err := c.SendLogHeartbeat(ctx, hb) 123 require.NoError(t, err) 124 assert.Equal(t, 0, len(cb.Commands)) 125 126 sc := pb.ScheduleCommand{ 127 UUID: s.ID(), 128 ServiceType: pb.DNService, 129 ShutdownStore: &pb.ShutdownStore{ 130 StoreID: "hello world", 131 }, 132 } 133 require.NoError(t, s.store.addScheduleCommands(ctx, 0, []pb.ScheduleCommand{sc})) 134 cb, err = c.SendLogHeartbeat(ctx, hb) 135 require.NoError(t, err) 136 require.Equal(t, 1, len(cb.Commands)) 137 require.Equal(t, sc, cb.Commands[0]) 138 } 139 runServiceTest(t, true, true, fn) 140 } 141 142 func TestHAKeeperClientSendCNHeartbeat(t *testing.T) { 143 fn := func(t *testing.T, s *Service) { 144 cfg := HAKeeperClientConfig{ 145 ServiceAddresses: []string{testServiceAddress}, 146 } 147 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 148 defer cancel() 149 c1, err := NewCNHAKeeperClient(ctx, cfg) 150 require.NoError(t, err) 151 defer func() { 152 assert.NoError(t, c1.Close()) 153 }() 154 155 // should be transparently handled 156 cc := c1.(*managedHAKeeperClient) 157 assert.NoError(t, cc.mu.client.close()) 158 cc.mu.client = nil 159 160 hb := pb.CNStoreHeartbeat{ 161 UUID: s.ID(), 162 ServiceAddress: "addr1", 163 } 164 _, err = c1.SendCNHeartbeat(ctx, hb) 165 require.NoError(t, err) 166 167 c2, err := NewDNHAKeeperClient(ctx, cfg) 168 require.NoError(t, err) 169 defer func() { 170 assert.NoError(t, c2.Close()) 171 }() 172 173 // should be transparently handled 174 cc = c2.(*managedHAKeeperClient) 175 assert.NoError(t, cc.mu.client.close()) 176 cc.mu.client = nil 177 178 hb2 := pb.DNStoreHeartbeat{ 179 UUID: s.ID(), 180 ServiceAddress: "addr2", 181 LogtailServerAddress: "addr3", 182 } 183 cb, err := c2.SendDNHeartbeat(ctx, hb2) 184 require.NoError(t, err) 185 assert.Equal(t, 0, len(cb.Commands)) 186 187 // should be transparently handled 188 cc = c1.(*managedHAKeeperClient) 189 assert.NoError(t, cc.mu.client.close()) 190 cc.mu.client = nil 191 192 cd, err := c1.GetClusterDetails(ctx) 193 require.NoError(t, err) 194 cn := pb.CNStore{ 195 UUID: s.ID(), 196 ServiceAddress: "addr1", 197 } 198 dn := pb.DNStore{ 199 UUID: s.ID(), 200 ServiceAddress: "addr2", 201 LogtailServerAddress: "addr3", 202 } 203 assert.Equal(t, []pb.CNStore{cn}, cd.CNStores) 204 assert.Equal(t, []pb.DNStore{dn}, cd.DNStores) 205 } 206 runServiceTest(t, true, true, fn) 207 } 208 209 func TestHAKeeperClientSendDNHeartbeat(t *testing.T) { 210 fn := func(t *testing.T, s *Service) { 211 cfg := HAKeeperClientConfig{ 212 ServiceAddresses: []string{testServiceAddress}, 213 } 214 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 215 defer cancel() 216 c, err := NewDNHAKeeperClient(ctx, cfg) 217 require.NoError(t, err) 218 defer func() { 219 assert.NoError(t, c.Close()) 220 }() 221 hb := pb.DNStoreHeartbeat{ 222 UUID: s.ID(), 223 } 224 cb, err := c.SendDNHeartbeat(ctx, hb) 225 require.NoError(t, err) 226 assert.Equal(t, 0, len(cb.Commands)) 227 228 sc := pb.ScheduleCommand{ 229 UUID: s.ID(), 230 ServiceType: pb.DNService, 231 ShutdownStore: &pb.ShutdownStore{ 232 StoreID: "hello world", 233 }, 234 } 235 require.NoError(t, s.store.addScheduleCommands(ctx, 0, []pb.ScheduleCommand{sc})) 236 cb, err = c.SendDNHeartbeat(ctx, hb) 237 require.NoError(t, err) 238 require.Equal(t, 1, len(cb.Commands)) 239 require.Equal(t, sc, cb.Commands[0]) 240 } 241 runServiceTest(t, true, true, fn) 242 } 243 244 func TestHAKeeperClientSendLogHeartbeat(t *testing.T) { 245 fn := func(t *testing.T, s *Service) { 246 cfg := HAKeeperClientConfig{ 247 ServiceAddresses: []string{testServiceAddress}, 248 } 249 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 250 defer cancel() 251 c, err := NewLogHAKeeperClient(ctx, cfg) 252 require.NoError(t, err) 253 defer func() { 254 assert.NoError(t, c.Close()) 255 }() 256 257 // should be transparently handled 258 cc := c.(*managedHAKeeperClient) 259 assert.NoError(t, cc.mu.client.close()) 260 cc.mu.client = nil 261 262 hb := s.store.getHeartbeatMessage() 263 cb, err := c.SendLogHeartbeat(ctx, hb) 264 require.NoError(t, err) 265 assert.Equal(t, 0, len(cb.Commands)) 266 267 sc := pb.ScheduleCommand{ 268 UUID: s.ID(), 269 ServiceType: pb.DNService, 270 ShutdownStore: &pb.ShutdownStore{ 271 StoreID: "hello world", 272 }, 273 } 274 require.NoError(t, s.store.addScheduleCommands(ctx, 0, []pb.ScheduleCommand{sc})) 275 cb, err = c.SendLogHeartbeat(ctx, hb) 276 require.NoError(t, err) 277 require.Equal(t, 1, len(cb.Commands)) 278 require.Equal(t, sc, cb.Commands[0]) 279 } 280 runServiceTest(t, true, true, fn) 281 } 282 283 func testNotHAKeeperErrorIsHandled(t *testing.T, fn func(*testing.T, *managedHAKeeperClient)) { 284 defer leaktest.AfterTest(t)() 285 cfg1 := Config{ 286 UUID: uuid.New().String(), 287 FS: vfs.NewStrictMem(), 288 DeploymentID: 1, 289 RTTMillisecond: 5, 290 DataDir: "data-1", 291 ServiceAddress: "127.0.0.1:9002", 292 RaftAddress: "127.0.0.1:9000", 293 GossipAddress: "127.0.0.1:9001", 294 GossipSeedAddresses: []string{"127.0.0.1:9011"}, 295 DisableWorkers: true, 296 } 297 cfg2 := Config{ 298 UUID: uuid.New().String(), 299 FS: vfs.NewStrictMem(), 300 DeploymentID: 1, 301 RTTMillisecond: 5, 302 DataDir: "data-2", 303 ServiceAddress: "127.0.0.1:9012", 304 RaftAddress: "127.0.0.1:9010", 305 GossipAddress: "127.0.0.1:9011", 306 GossipSeedAddresses: []string{"127.0.0.1:9001"}, 307 DisableWorkers: true, 308 } 309 cfg1.Fill() 310 service1, err := NewService(cfg1, 311 testutil.NewFS(), 312 WithBackendFilter(func(msg morpc.Message, backendAddr string) bool { 313 return true 314 }), 315 ) 316 require.NoError(t, err) 317 defer func() { 318 assert.NoError(t, service1.Close()) 319 }() 320 cfg2.Fill() 321 service2, err := NewService(cfg2, 322 testutil.NewFS(), 323 WithBackendFilter(func(msg morpc.Message, backendAddr string) bool { 324 return true 325 }), 326 ) 327 require.NoError(t, err) 328 defer func() { 329 assert.NoError(t, service2.Close()) 330 }() 331 // service2 is HAKeeper 332 peers := make(map[uint64]dragonboat.Target) 333 peers[1] = service2.ID() 334 assert.NoError(t, service2.store.startHAKeeperReplica(1, peers, false)) 335 // manually construct a HAKeeper client that is connected to service1 336 pool := &sync.Pool{} 337 pool.New = func() interface{} { 338 return &RPCRequest{pool: pool} 339 } 340 respPool := &sync.Pool{} 341 respPool.New = func() interface{} { 342 return &RPCResponse{pool: respPool} 343 } 344 cfg := HAKeeperClientConfig{ 345 ServiceAddresses: []string{cfg1.ServiceAddress, cfg2.ServiceAddress}, 346 } 347 c := &hakeeperClient{ 348 cfg: cfg, 349 pool: pool, 350 respPool: respPool, 351 } 352 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 353 defer cancel() 354 cc, err := getRPCClient(ctx, cfg1.ServiceAddress, c.respPool, defaultMaxMessageSize, false) 355 require.NoError(t, err) 356 c.addr = cfg1.ServiceAddress 357 c.client = cc 358 client := &managedHAKeeperClient{cfg: cfg} 359 client.mu.client = c 360 defer func() { 361 require.NoError(t, client.Close()) 362 }() 363 fn(t, client) 364 } 365 366 func TestGetClusterDetailsWhenNotConnectedToHAKeeper(t *testing.T) { 367 fn := func(t *testing.T, c *managedHAKeeperClient) { 368 oldc := c.mu.client 369 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 370 defer cancel() 371 _, err := c.GetClusterDetails(ctx) 372 require.NoError(t, err) 373 require.True(t, oldc != c.mu.client) 374 } 375 testNotHAKeeperErrorIsHandled(t, fn) 376 } 377 378 func TestSendCNHeartbeatWhenNotConnectedToHAKeeper(t *testing.T) { 379 fn := func(t *testing.T, c *managedHAKeeperClient) { 380 oldc := c.mu.client 381 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 382 defer cancel() 383 _, err := c.SendCNHeartbeat(ctx, pb.CNStoreHeartbeat{}) 384 require.NoError(t, err) 385 require.True(t, oldc != c.mu.client) 386 } 387 testNotHAKeeperErrorIsHandled(t, fn) 388 } 389 390 func TestSendDNHeartbeatWhenNotConnectedToHAKeeper(t *testing.T) { 391 fn := func(t *testing.T, c *managedHAKeeperClient) { 392 oldc := c.mu.client 393 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 394 defer cancel() 395 _, err := c.SendDNHeartbeat(ctx, pb.DNStoreHeartbeat{}) 396 require.NoError(t, err) 397 require.True(t, oldc != c.mu.client) 398 } 399 testNotHAKeeperErrorIsHandled(t, fn) 400 } 401 402 func TestSendLogHeartbeatWhenNotConnectedToHAKeeper(t *testing.T) { 403 fn := func(t *testing.T, c *managedHAKeeperClient) { 404 oldc := c.mu.client 405 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 406 defer cancel() 407 _, err := c.SendLogHeartbeat(ctx, pb.LogStoreHeartbeat{}) 408 require.NoError(t, err) 409 require.True(t, oldc != c.mu.client) 410 } 411 testNotHAKeeperErrorIsHandled(t, fn) 412 }