github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/balancer_switching_test.go (about) 1 /* 2 * 3 * Copyright 2017 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 package grpc 20 21 import ( 22 "context" 23 "fmt" 24 "math" 25 "testing" 26 "time" 27 28 "github.com/hxx258456/ccgo/grpc/balancer" 29 "github.com/hxx258456/ccgo/grpc/balancer/roundrobin" 30 "github.com/hxx258456/ccgo/grpc/internal" 31 "github.com/hxx258456/ccgo/grpc/internal/balancer/stub" 32 "github.com/hxx258456/ccgo/grpc/resolver" 33 "github.com/hxx258456/ccgo/grpc/resolver/manual" 34 "github.com/hxx258456/ccgo/grpc/serviceconfig" 35 ) 36 37 var _ balancer.Builder = &magicalLB{} 38 var _ balancer.Balancer = &magicalLB{} 39 40 // magicalLB is a ringer for grpclb. It is used to avoid circular dependencies on the grpclb package 41 type magicalLB struct{} 42 43 func (b *magicalLB) Name() string { 44 return "grpclb" 45 } 46 47 func (b *magicalLB) Build(cc balancer.ClientConn, opts balancer.BuildOptions) balancer.Balancer { 48 return b 49 } 50 51 func (b *magicalLB) ResolverError(error) {} 52 53 func (b *magicalLB) UpdateSubConnState(balancer.SubConn, balancer.SubConnState) {} 54 55 func (b *magicalLB) UpdateClientConnState(balancer.ClientConnState) error { 56 return nil 57 } 58 59 func (b *magicalLB) Close() {} 60 61 func (b *magicalLB) ExitIdle() {} 62 63 func init() { 64 balancer.Register(&magicalLB{}) 65 } 66 67 func startServers(t *testing.T, numServers int, maxStreams uint32) ([]*server, func()) { 68 var servers []*server 69 for i := 0; i < numServers; i++ { 70 s := newTestServer() 71 servers = append(servers, s) 72 go s.start(t, 0, maxStreams) 73 s.wait(t, 2*time.Second) 74 } 75 return servers, func() { 76 for i := 0; i < numServers; i++ { 77 servers[i].stop() 78 } 79 } 80 } 81 82 func checkPickFirst(cc *ClientConn, servers []*server) error { 83 var ( 84 req = "port" 85 reply string 86 err error 87 ) 88 connected := false 89 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 90 defer cancel() 91 for i := 0; i < 5000; i++ { 92 if err = cc.Invoke(ctx, "/foo/bar", &req, &reply); errorDesc(err) == servers[0].port { 93 if connected { 94 // connected is set to false if peer is not server[0]. So if 95 // connected is true here, this is the second time we saw 96 // server[0] in a row. Break because pickfirst is in effect. 97 break 98 } 99 connected = true 100 } else { 101 connected = false 102 } 103 time.Sleep(time.Millisecond) 104 } 105 if !connected { 106 return fmt.Errorf("pickfirst is not in effect after 5 second, EmptyCall() = _, %v, want _, %v", err, servers[0].port) 107 } 108 109 // The following RPCs should all succeed with the first server. 110 for i := 0; i < 3; i++ { 111 err = cc.Invoke(ctx, "/foo/bar", &req, &reply) 112 if errorDesc(err) != servers[0].port { 113 return fmt.Errorf("index %d: want peer %v, got peer %v", i, servers[0].port, err) 114 } 115 } 116 return nil 117 } 118 119 func checkRoundRobin(cc *ClientConn, servers []*server) error { 120 var ( 121 req = "port" 122 reply string 123 err error 124 ) 125 126 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 127 defer cancel() 128 // Make sure connections to all servers are up. 129 for i := 0; i < 2; i++ { 130 // Do this check twice, otherwise the first RPC's transport may still be 131 // picked by the closing pickfirst balancer, and the test becomes flaky. 132 for _, s := range servers { 133 var up bool 134 for i := 0; i < 5000; i++ { 135 if err = cc.Invoke(ctx, "/foo/bar", &req, &reply); errorDesc(err) == s.port { 136 up = true 137 break 138 } 139 time.Sleep(time.Millisecond) 140 } 141 if !up { 142 return fmt.Errorf("server %v is not up within 5 second", s.port) 143 } 144 } 145 } 146 147 serverCount := len(servers) 148 for i := 0; i < 3*serverCount; i++ { 149 err = cc.Invoke(ctx, "/foo/bar", &req, &reply) 150 if errorDesc(err) != servers[i%serverCount].port { 151 return fmt.Errorf("index %d: want peer %v, got peer %v", i, servers[i%serverCount].port, err) 152 } 153 } 154 return nil 155 } 156 157 func (s) TestSwitchBalancer(t *testing.T) { 158 r := manual.NewBuilderWithScheme("whatever") 159 160 const numServers = 2 161 servers, scleanup := startServers(t, numServers, math.MaxInt32) 162 defer scleanup() 163 164 cc, err := Dial(r.Scheme()+":///test.server", WithInsecure(), WithResolvers(r), WithCodec(testCodec{})) 165 if err != nil { 166 t.Fatalf("failed to dial: %v", err) 167 } 168 defer cc.Close() 169 addrs := []resolver.Address{{Addr: servers[0].addr}, {Addr: servers[1].addr}} 170 r.UpdateState(resolver.State{Addresses: addrs}) 171 // The default balancer is pickfirst. 172 if err := checkPickFirst(cc, servers); err != nil { 173 t.Fatalf("check pickfirst returned non-nil error: %v", err) 174 } 175 // Switch to roundrobin. 176 cc.updateResolverState(resolver.State{ServiceConfig: parseCfg(r, `{"loadBalancingPolicy": "round_robin"}`), Addresses: addrs}, nil) 177 if err := checkRoundRobin(cc, servers); err != nil { 178 t.Fatalf("check roundrobin returned non-nil error: %v", err) 179 } 180 // Switch to pickfirst. 181 cc.updateResolverState(resolver.State{ServiceConfig: parseCfg(r, `{"loadBalancingPolicy": "pick_first"}`), Addresses: addrs}, nil) 182 if err := checkPickFirst(cc, servers); err != nil { 183 t.Fatalf("check pickfirst returned non-nil error: %v", err) 184 } 185 } 186 187 // Test that balancer specified by dial option will not be overridden. 188 func (s) TestBalancerDialOption(t *testing.T) { 189 r := manual.NewBuilderWithScheme("whatever") 190 191 const numServers = 2 192 servers, scleanup := startServers(t, numServers, math.MaxInt32) 193 defer scleanup() 194 195 cc, err := Dial(r.Scheme()+":///test.server", WithInsecure(), WithResolvers(r), WithCodec(testCodec{}), WithBalancerName(roundrobin.Name)) 196 if err != nil { 197 t.Fatalf("failed to dial: %v", err) 198 } 199 defer cc.Close() 200 addrs := []resolver.Address{{Addr: servers[0].addr}, {Addr: servers[1].addr}} 201 r.UpdateState(resolver.State{Addresses: addrs}) 202 // The init balancer is roundrobin. 203 if err := checkRoundRobin(cc, servers); err != nil { 204 t.Fatalf("check roundrobin returned non-nil error: %v", err) 205 } 206 // Switch to pickfirst. 207 cc.updateResolverState(resolver.State{ServiceConfig: parseCfg(r, `{"loadBalancingPolicy": "pick_first"}`), Addresses: addrs}, nil) 208 // Balancer is still roundrobin. 209 if err := checkRoundRobin(cc, servers); err != nil { 210 t.Fatalf("check roundrobin returned non-nil error: %v", err) 211 } 212 } 213 214 // First addr update contains grpclb. 215 func (s) TestSwitchBalancerGRPCLBFirst(t *testing.T) { 216 r := manual.NewBuilderWithScheme("whatever") 217 218 cc, err := Dial(r.Scheme()+":///test.server", WithInsecure(), WithResolvers(r), WithCodec(testCodec{})) 219 if err != nil { 220 t.Fatalf("failed to dial: %v", err) 221 } 222 defer cc.Close() 223 224 // ClientConn will switch balancer to grpclb when receives an address of 225 // type GRPCLB. 226 r.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: "backend"}, {Addr: "grpclb", Type: resolver.GRPCLB}}}) 227 var isGRPCLB bool 228 for i := 0; i < 5000; i++ { 229 cc.mu.Lock() 230 isGRPCLB = cc.curBalancerName == "grpclb" 231 cc.mu.Unlock() 232 if isGRPCLB { 233 break 234 } 235 time.Sleep(time.Millisecond) 236 } 237 if !isGRPCLB { 238 t.Fatalf("after 5 second, cc.balancer is of type %v, not grpclb", cc.curBalancerName) 239 } 240 241 // New update containing new backend and new grpclb. Should not switch 242 // balancer. 243 r.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: "backend2"}, {Addr: "grpclb2", Type: resolver.GRPCLB}}}) 244 for i := 0; i < 200; i++ { 245 cc.mu.Lock() 246 isGRPCLB = cc.curBalancerName == "grpclb" 247 cc.mu.Unlock() 248 if !isGRPCLB { 249 break 250 } 251 time.Sleep(time.Millisecond) 252 } 253 if !isGRPCLB { 254 t.Fatalf("within 200 ms, cc.balancer switched to !grpclb, want grpclb") 255 } 256 257 var isPickFirst bool 258 // Switch balancer to pickfirst. 259 r.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: "backend"}}}) 260 for i := 0; i < 5000; i++ { 261 cc.mu.Lock() 262 isPickFirst = cc.curBalancerName == PickFirstBalancerName 263 cc.mu.Unlock() 264 if isPickFirst { 265 break 266 } 267 time.Sleep(time.Millisecond) 268 } 269 if !isPickFirst { 270 t.Fatalf("after 5 second, cc.balancer is of type %v, not pick_first", cc.curBalancerName) 271 } 272 } 273 274 // First addr update does not contain grpclb. 275 func (s) TestSwitchBalancerGRPCLBSecond(t *testing.T) { 276 r := manual.NewBuilderWithScheme("whatever") 277 278 cc, err := Dial(r.Scheme()+":///test.server", WithInsecure(), WithResolvers(r), WithCodec(testCodec{})) 279 if err != nil { 280 t.Fatalf("failed to dial: %v", err) 281 } 282 defer cc.Close() 283 284 r.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: "backend"}}}) 285 var isPickFirst bool 286 for i := 0; i < 5000; i++ { 287 cc.mu.Lock() 288 isPickFirst = cc.curBalancerName == PickFirstBalancerName 289 cc.mu.Unlock() 290 if isPickFirst { 291 break 292 } 293 time.Sleep(time.Millisecond) 294 } 295 if !isPickFirst { 296 t.Fatalf("after 5 second, cc.balancer is of type %v, not pick_first", cc.curBalancerName) 297 } 298 299 // ClientConn will switch balancer to grpclb when receives an address of 300 // type GRPCLB. 301 r.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: "backend"}, {Addr: "grpclb", Type: resolver.GRPCLB}}}) 302 var isGRPCLB bool 303 for i := 0; i < 5000; i++ { 304 cc.mu.Lock() 305 isGRPCLB = cc.curBalancerName == "grpclb" 306 cc.mu.Unlock() 307 if isGRPCLB { 308 break 309 } 310 time.Sleep(time.Millisecond) 311 } 312 if !isGRPCLB { 313 t.Fatalf("after 5 second, cc.balancer is of type %v, not grpclb", cc.curBalancerName) 314 } 315 316 // New update containing new backend and new grpclb. Should not switch 317 // balancer. 318 r.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: "backend2"}, {Addr: "grpclb2", Type: resolver.GRPCLB}}}) 319 for i := 0; i < 200; i++ { 320 cc.mu.Lock() 321 isGRPCLB = cc.curBalancerName == "grpclb" 322 cc.mu.Unlock() 323 if !isGRPCLB { 324 break 325 } 326 time.Sleep(time.Millisecond) 327 } 328 if !isGRPCLB { 329 t.Fatalf("within 200 ms, cc.balancer switched to !grpclb, want grpclb") 330 } 331 332 // Switch balancer back. 333 r.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: "backend"}}}) 334 for i := 0; i < 5000; i++ { 335 cc.mu.Lock() 336 isPickFirst = cc.curBalancerName == PickFirstBalancerName 337 cc.mu.Unlock() 338 if isPickFirst { 339 break 340 } 341 time.Sleep(time.Millisecond) 342 } 343 if !isPickFirst { 344 t.Fatalf("after 5 second, cc.balancer is of type %v, not pick_first", cc.curBalancerName) 345 } 346 } 347 348 // Test that if the current balancer is roundrobin, after switching to grpclb, 349 // when the resolved address doesn't contain grpclb addresses, balancer will be 350 // switched back to roundrobin. 351 func (s) TestSwitchBalancerGRPCLBRoundRobin(t *testing.T) { 352 r := manual.NewBuilderWithScheme("whatever") 353 354 cc, err := Dial(r.Scheme()+":///test.server", WithInsecure(), WithResolvers(r), WithCodec(testCodec{})) 355 if err != nil { 356 t.Fatalf("failed to dial: %v", err) 357 } 358 defer cc.Close() 359 360 sc := parseCfg(r, `{"loadBalancingPolicy": "round_robin"}`) 361 362 r.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: "backend"}}, ServiceConfig: sc}) 363 var isRoundRobin bool 364 for i := 0; i < 5000; i++ { 365 cc.mu.Lock() 366 isRoundRobin = cc.curBalancerName == "round_robin" 367 cc.mu.Unlock() 368 if isRoundRobin { 369 break 370 } 371 time.Sleep(time.Millisecond) 372 } 373 if !isRoundRobin { 374 t.Fatalf("after 5 second, cc.balancer is of type %v, not round_robin", cc.curBalancerName) 375 } 376 377 // ClientConn will switch balancer to grpclb when receives an address of 378 // type GRPCLB. 379 r.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: "grpclb", Type: resolver.GRPCLB}}, ServiceConfig: sc}) 380 var isGRPCLB bool 381 for i := 0; i < 5000; i++ { 382 cc.mu.Lock() 383 isGRPCLB = cc.curBalancerName == "grpclb" 384 cc.mu.Unlock() 385 if isGRPCLB { 386 break 387 } 388 time.Sleep(time.Millisecond) 389 } 390 if !isGRPCLB { 391 t.Fatalf("after 5 second, cc.balancer is of type %v, not grpclb", cc.curBalancerName) 392 } 393 394 // Switch balancer back. 395 r.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: "backend"}}, ServiceConfig: sc}) 396 for i := 0; i < 5000; i++ { 397 cc.mu.Lock() 398 isRoundRobin = cc.curBalancerName == "round_robin" 399 cc.mu.Unlock() 400 if isRoundRobin { 401 break 402 } 403 time.Sleep(time.Millisecond) 404 } 405 if !isRoundRobin { 406 t.Fatalf("after 5 second, cc.balancer is of type %v, not round_robin", cc.curBalancerName) 407 } 408 } 409 410 // Test that if resolved address list contains grpclb, the balancer option in 411 // service config won't take effect. But when there's no grpclb address in a new 412 // resolved address list, balancer will be switched to the new one. 413 func (s) TestSwitchBalancerGRPCLBServiceConfig(t *testing.T) { 414 r := manual.NewBuilderWithScheme("whatever") 415 416 cc, err := Dial(r.Scheme()+":///test.server", WithInsecure(), WithResolvers(r), WithCodec(testCodec{})) 417 if err != nil { 418 t.Fatalf("failed to dial: %v", err) 419 } 420 defer cc.Close() 421 422 r.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: "backend"}}}) 423 var isPickFirst bool 424 for i := 0; i < 5000; i++ { 425 cc.mu.Lock() 426 isPickFirst = cc.curBalancerName == PickFirstBalancerName 427 cc.mu.Unlock() 428 if isPickFirst { 429 break 430 } 431 time.Sleep(time.Millisecond) 432 } 433 if !isPickFirst { 434 t.Fatalf("after 5 second, cc.balancer is of type %v, not pick_first", cc.curBalancerName) 435 } 436 437 // ClientConn will switch balancer to grpclb when receives an address of 438 // type GRPCLB. 439 addrs := []resolver.Address{{Addr: "grpclb", Type: resolver.GRPCLB}} 440 r.UpdateState(resolver.State{Addresses: addrs}) 441 var isGRPCLB bool 442 for i := 0; i < 5000; i++ { 443 cc.mu.Lock() 444 isGRPCLB = cc.curBalancerName == "grpclb" 445 cc.mu.Unlock() 446 if isGRPCLB { 447 break 448 } 449 time.Sleep(time.Millisecond) 450 } 451 if !isGRPCLB { 452 t.Fatalf("after 5 second, cc.balancer is of type %v, not grpclb", cc.curBalancerName) 453 } 454 455 sc := parseCfg(r, `{"loadBalancingPolicy": "round_robin"}`) 456 r.UpdateState(resolver.State{Addresses: addrs, ServiceConfig: sc}) 457 var isRoundRobin bool 458 for i := 0; i < 200; i++ { 459 cc.mu.Lock() 460 isRoundRobin = cc.curBalancerName == "round_robin" 461 cc.mu.Unlock() 462 if isRoundRobin { 463 break 464 } 465 time.Sleep(time.Millisecond) 466 } 467 // Balancer should NOT switch to round_robin because resolved list contains 468 // grpclb. 469 if isRoundRobin { 470 t.Fatalf("within 200 ms, cc.balancer switched to round_robin, want grpclb") 471 } 472 473 // Switch balancer back. 474 r.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: "backend"}}, ServiceConfig: sc}) 475 for i := 0; i < 5000; i++ { 476 cc.mu.Lock() 477 isRoundRobin = cc.curBalancerName == "round_robin" 478 cc.mu.Unlock() 479 if isRoundRobin { 480 break 481 } 482 time.Sleep(time.Millisecond) 483 } 484 if !isRoundRobin { 485 t.Fatalf("after 5 second, cc.balancer is of type %v, not round_robin", cc.curBalancerName) 486 } 487 } 488 489 // Test that when switching to grpclb fails because grpclb is not registered, 490 // the fallback balancer will only get backend addresses, not the grpclb server 491 // address. 492 // 493 // The tests sends 3 server addresses (all backends) as resolved addresses, but 494 // claim the first one is grpclb server. The all RPCs should all be send to the 495 // other addresses, not the first one. 496 func (s) TestSwitchBalancerGRPCLBWithGRPCLBNotRegistered(t *testing.T) { 497 internal.BalancerUnregister("grpclb") 498 defer balancer.Register(&magicalLB{}) 499 500 r := manual.NewBuilderWithScheme("whatever") 501 502 const numServers = 3 503 servers, scleanup := startServers(t, numServers, math.MaxInt32) 504 defer scleanup() 505 506 cc, err := Dial(r.Scheme()+":///test.server", WithInsecure(), WithResolvers(r), WithCodec(testCodec{})) 507 if err != nil { 508 t.Fatalf("failed to dial: %v", err) 509 } 510 defer cc.Close() 511 r.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: servers[1].addr}, {Addr: servers[2].addr}}}) 512 // The default balancer is pickfirst. 513 if err := checkPickFirst(cc, servers[1:]); err != nil { 514 t.Fatalf("check pickfirst returned non-nil error: %v", err) 515 } 516 // Try switching to grpclb by sending servers[0] as grpclb address. It's 517 // expected that servers[0] will be filtered out, so it will not be used by 518 // the balancer. 519 // 520 // If the filtering failed, servers[0] will be used for RPCs and the RPCs 521 // will succeed. The following checks will catch this and fail. 522 addrs := []resolver.Address{ 523 {Addr: servers[0].addr, Type: resolver.GRPCLB}, 524 {Addr: servers[1].addr}, {Addr: servers[2].addr}} 525 r.UpdateState(resolver.State{Addresses: addrs}) 526 // Still check for pickfirst, but only with server[1] and server[2]. 527 if err := checkPickFirst(cc, servers[1:]); err != nil { 528 t.Fatalf("check pickfirst returned non-nil error: %v", err) 529 } 530 // Switch to roundrobin, and check against server[1] and server[2]. 531 cc.updateResolverState(resolver.State{ServiceConfig: parseCfg(r, `{"loadBalancingPolicy": "round_robin"}`), Addresses: addrs}, nil) 532 if err := checkRoundRobin(cc, servers[1:]); err != nil { 533 t.Fatalf("check roundrobin returned non-nil error: %v", err) 534 } 535 } 536 537 const inlineRemoveSubConnBalancerName = "test-inline-remove-subconn-balancer" 538 539 func init() { 540 stub.Register(inlineRemoveSubConnBalancerName, stub.BalancerFuncs{ 541 Close: func(data *stub.BalancerData) { 542 data.ClientConn.RemoveSubConn(&acBalancerWrapper{}) 543 }, 544 }) 545 } 546 547 // Test that when switching to balancers, the old balancer calls RemoveSubConn 548 // in Close. 549 // 550 // This test is to make sure this close doesn't cause a deadlock. 551 func (s) TestSwitchBalancerOldRemoveSubConn(t *testing.T) { 552 r := manual.NewBuilderWithScheme("whatever") 553 cc, err := Dial(r.Scheme()+":///test.server", WithInsecure(), WithResolvers(r)) 554 if err != nil { 555 t.Fatalf("failed to dial: %v", err) 556 } 557 defer cc.Close() 558 cc.updateResolverState(resolver.State{ServiceConfig: parseCfg(r, fmt.Sprintf(`{"loadBalancingPolicy": "%v"}`, inlineRemoveSubConnBalancerName))}, nil) 559 // This service config update will switch balancer from 560 // "test-inline-remove-subconn-balancer" to "pick_first". The test balancer 561 // will be closed, which will call cc.RemoveSubConn() inline (this 562 // RemoveSubConn is not required by the API, but some balancers might do 563 // it). 564 // 565 // This is to make sure the cc.RemoveSubConn() from Close() doesn't cause a 566 // deadlock (e.g. trying to grab a mutex while it's already locked). 567 // 568 // Do it in a goroutine so this test will fail with a helpful message 569 // (though the goroutine will still leak). 570 done := make(chan struct{}) 571 go func() { 572 cc.updateResolverState(resolver.State{ServiceConfig: parseCfg(r, `{"loadBalancingPolicy": "pick_first"}`)}, nil) 573 close(done) 574 }() 575 select { 576 case <-time.After(defaultTestTimeout): 577 t.Fatalf("timeout waiting for updateResolverState to finish") 578 case <-done: 579 } 580 } 581 582 func parseCfg(r *manual.Resolver, s string) *serviceconfig.ParseResult { 583 scpr := r.CC.ParseServiceConfig(s) 584 if scpr.Err != nil { 585 panic(fmt.Sprintf("Error parsing config %q: %v", s, scpr.Err)) 586 } 587 return scpr 588 }