github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/clientconn_state_transition_test.go (about) 1 /* 2 * 3 * Copyright 2018 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 "net" 24 "sync" 25 "testing" 26 "time" 27 28 "github.com/hxx258456/ccgo/grpc/balancer" 29 "github.com/hxx258456/ccgo/grpc/connectivity" 30 "github.com/hxx258456/ccgo/grpc/internal/testutils" 31 "github.com/hxx258456/ccgo/grpc/resolver" 32 "github.com/hxx258456/ccgo/grpc/resolver/manual" 33 "github.com/hxx258456/ccgo/net/http2" 34 ) 35 36 const stateRecordingBalancerName = "state_recoding_balancer" 37 38 var testBalancerBuilder = newStateRecordingBalancerBuilder() 39 40 func init() { 41 balancer.Register(testBalancerBuilder) 42 } 43 44 // These tests use a pipeListener. This listener is similar to net.Listener 45 // except that it is unbuffered, so each read and write will wait for the other 46 // side's corresponding write or read. 47 func (s) TestStateTransitions_SingleAddress(t *testing.T) { 48 for _, test := range []struct { 49 desc string 50 want []connectivity.State 51 server func(net.Listener) net.Conn 52 }{ 53 { 54 desc: "When the server returns server preface, the client enters READY.", 55 want: []connectivity.State{ 56 connectivity.Connecting, 57 connectivity.Ready, 58 }, 59 server: func(lis net.Listener) net.Conn { 60 conn, err := lis.Accept() 61 if err != nil { 62 t.Error(err) 63 return nil 64 } 65 66 go keepReading(conn) 67 68 framer := http2.NewFramer(conn, conn) 69 if err := framer.WriteSettings(http2.Setting{}); err != nil { 70 t.Errorf("Error while writing settings frame. %v", err) 71 return nil 72 } 73 74 return conn 75 }, 76 }, 77 { 78 desc: "When the connection is closed before the preface is sent, the client enters TRANSIENT FAILURE.", 79 want: []connectivity.State{ 80 connectivity.Connecting, 81 connectivity.TransientFailure, 82 }, 83 server: func(lis net.Listener) net.Conn { 84 conn, err := lis.Accept() 85 if err != nil { 86 t.Error(err) 87 return nil 88 } 89 90 conn.Close() 91 return nil 92 }, 93 }, 94 { 95 desc: `When the server sends its connection preface, but the connection dies before the client can write its 96 connection preface, the client enters TRANSIENT FAILURE.`, 97 want: []connectivity.State{ 98 connectivity.Connecting, 99 connectivity.TransientFailure, 100 }, 101 server: func(lis net.Listener) net.Conn { 102 conn, err := lis.Accept() 103 if err != nil { 104 t.Error(err) 105 return nil 106 } 107 108 framer := http2.NewFramer(conn, conn) 109 if err := framer.WriteSettings(http2.Setting{}); err != nil { 110 t.Errorf("Error while writing settings frame. %v", err) 111 return nil 112 } 113 114 conn.Close() 115 return nil 116 }, 117 }, 118 { 119 desc: `When the server reads the client connection preface but does not send its connection preface, the 120 client enters TRANSIENT FAILURE.`, 121 want: []connectivity.State{ 122 connectivity.Connecting, 123 connectivity.TransientFailure, 124 }, 125 server: func(lis net.Listener) net.Conn { 126 conn, err := lis.Accept() 127 if err != nil { 128 t.Error(err) 129 return nil 130 } 131 132 go keepReading(conn) 133 134 return conn 135 }, 136 }, 137 } { 138 t.Log(test.desc) 139 testStateTransitionSingleAddress(t, test.want, test.server) 140 } 141 } 142 143 func testStateTransitionSingleAddress(t *testing.T, want []connectivity.State, server func(net.Listener) net.Conn) { 144 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) 145 defer cancel() 146 147 pl := testutils.NewPipeListener() 148 defer pl.Close() 149 150 // Launch the server. 151 var conn net.Conn 152 var connMu sync.Mutex 153 go func() { 154 connMu.Lock() 155 conn = server(pl) 156 connMu.Unlock() 157 }() 158 159 client, err := DialContext(ctx, 160 "", 161 WithInsecure(), 162 WithBalancerName(stateRecordingBalancerName), 163 WithDialer(pl.Dialer()), 164 withBackoff(noBackoff{}), 165 withMinConnectDeadline(func() time.Duration { return time.Millisecond * 100 })) 166 if err != nil { 167 t.Fatal(err) 168 } 169 defer client.Close() 170 go stayConnected(client) 171 172 stateNotifications := testBalancerBuilder.nextStateNotifier() 173 174 timeout := time.After(5 * time.Second) 175 176 for i := 0; i < len(want); i++ { 177 select { 178 case <-timeout: 179 t.Fatalf("timed out waiting for state %d (%v) in flow %v", i, want[i], want) 180 case seen := <-stateNotifications: 181 if seen != want[i] { 182 t.Fatalf("expected to see %v at position %d in flow %v, got %v", want[i], i, want, seen) 183 } 184 } 185 } 186 187 connMu.Lock() 188 defer connMu.Unlock() 189 if conn != nil { 190 err = conn.Close() 191 if err != nil { 192 t.Fatal(err) 193 } 194 } 195 } 196 197 // When a READY connection is closed, the client enters IDLE then CONNECTING. 198 func (s) TestStateTransitions_ReadyToConnecting(t *testing.T) { 199 want := []connectivity.State{ 200 connectivity.Connecting, 201 connectivity.Ready, 202 connectivity.Idle, 203 connectivity.Connecting, 204 } 205 206 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) 207 defer cancel() 208 209 lis, err := net.Listen("tcp", "localhost:0") 210 if err != nil { 211 t.Fatalf("Error while listening. Err: %v", err) 212 } 213 defer lis.Close() 214 215 sawReady := make(chan struct{}, 1) 216 defer close(sawReady) 217 218 // Launch the server. 219 go func() { 220 conn, err := lis.Accept() 221 if err != nil { 222 t.Error(err) 223 return 224 } 225 226 go keepReading(conn) 227 228 framer := http2.NewFramer(conn, conn) 229 if err := framer.WriteSettings(http2.Setting{}); err != nil { 230 t.Errorf("Error while writing settings frame. %v", err) 231 return 232 } 233 234 // Prevents race between onPrefaceReceipt and onClose. 235 <-sawReady 236 237 conn.Close() 238 }() 239 240 client, err := DialContext(ctx, lis.Addr().String(), WithInsecure(), WithBalancerName(stateRecordingBalancerName)) 241 if err != nil { 242 t.Fatal(err) 243 } 244 defer client.Close() 245 go stayConnected(client) 246 247 stateNotifications := testBalancerBuilder.nextStateNotifier() 248 249 timeout := time.After(5 * time.Second) 250 251 for i := 0; i < len(want); i++ { 252 select { 253 case <-timeout: 254 t.Fatalf("timed out waiting for state %d (%v) in flow %v", i, want[i], want) 255 case seen := <-stateNotifications: 256 if seen == connectivity.Ready { 257 sawReady <- struct{}{} 258 } 259 if seen != want[i] { 260 t.Fatalf("expected to see %v at position %d in flow %v, got %v", want[i], i, want, seen) 261 } 262 } 263 } 264 } 265 266 // When the first connection is closed, the client stays in CONNECTING until it 267 // tries the second address (which succeeds, and then it enters READY). 268 func (s) TestStateTransitions_TriesAllAddrsBeforeTransientFailure(t *testing.T) { 269 want := []connectivity.State{ 270 connectivity.Connecting, 271 connectivity.Ready, 272 } 273 274 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) 275 defer cancel() 276 277 lis1, err := net.Listen("tcp", "localhost:0") 278 if err != nil { 279 t.Fatalf("Error while listening. Err: %v", err) 280 } 281 defer lis1.Close() 282 283 lis2, err := net.Listen("tcp", "localhost:0") 284 if err != nil { 285 t.Fatalf("Error while listening. Err: %v", err) 286 } 287 defer lis2.Close() 288 289 server1Done := make(chan struct{}) 290 server2Done := make(chan struct{}) 291 292 // Launch server 1. 293 go func() { 294 conn, err := lis1.Accept() 295 if err != nil { 296 t.Error(err) 297 return 298 } 299 300 conn.Close() 301 close(server1Done) 302 }() 303 // Launch server 2. 304 go func() { 305 conn, err := lis2.Accept() 306 if err != nil { 307 t.Error(err) 308 return 309 } 310 311 go keepReading(conn) 312 313 framer := http2.NewFramer(conn, conn) 314 if err := framer.WriteSettings(http2.Setting{}); err != nil { 315 t.Errorf("Error while writing settings frame. %v", err) 316 return 317 } 318 319 close(server2Done) 320 }() 321 322 rb := manual.NewBuilderWithScheme("whatever") 323 rb.InitialState(resolver.State{Addresses: []resolver.Address{ 324 {Addr: lis1.Addr().String()}, 325 {Addr: lis2.Addr().String()}, 326 }}) 327 client, err := DialContext(ctx, "whatever:///this-gets-overwritten", WithInsecure(), WithBalancerName(stateRecordingBalancerName), WithResolvers(rb)) 328 if err != nil { 329 t.Fatal(err) 330 } 331 defer client.Close() 332 333 stateNotifications := testBalancerBuilder.nextStateNotifier() 334 335 timeout := time.After(5 * time.Second) 336 337 for i := 0; i < len(want); i++ { 338 select { 339 case <-timeout: 340 t.Fatalf("timed out waiting for state %d (%v) in flow %v", i, want[i], want) 341 case seen := <-stateNotifications: 342 if seen != want[i] { 343 t.Fatalf("expected to see %v at position %d in flow %v, got %v", want[i], i, want, seen) 344 } 345 } 346 } 347 select { 348 case <-timeout: 349 t.Fatal("saw the correct state transitions, but timed out waiting for client to finish interactions with server 1") 350 case <-server1Done: 351 } 352 select { 353 case <-timeout: 354 t.Fatal("saw the correct state transitions, but timed out waiting for client to finish interactions with server 2") 355 case <-server2Done: 356 } 357 } 358 359 // When there are multiple addresses, and we enter READY on one of them, a 360 // later closure should cause the client to enter CONNECTING 361 func (s) TestStateTransitions_MultipleAddrsEntersReady(t *testing.T) { 362 want := []connectivity.State{ 363 connectivity.Connecting, 364 connectivity.Ready, 365 connectivity.Idle, 366 connectivity.Connecting, 367 } 368 369 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) 370 defer cancel() 371 372 lis1, err := net.Listen("tcp", "localhost:0") 373 if err != nil { 374 t.Fatalf("Error while listening. Err: %v", err) 375 } 376 defer lis1.Close() 377 378 // Never actually gets used; we just want it to be alive so that the resolver has two addresses to target. 379 lis2, err := net.Listen("tcp", "localhost:0") 380 if err != nil { 381 t.Fatalf("Error while listening. Err: %v", err) 382 } 383 defer lis2.Close() 384 385 server1Done := make(chan struct{}) 386 sawReady := make(chan struct{}, 1) 387 defer close(sawReady) 388 389 // Launch server 1. 390 go func() { 391 conn, err := lis1.Accept() 392 if err != nil { 393 t.Error(err) 394 return 395 } 396 397 go keepReading(conn) 398 399 framer := http2.NewFramer(conn, conn) 400 if err := framer.WriteSettings(http2.Setting{}); err != nil { 401 t.Errorf("Error while writing settings frame. %v", err) 402 return 403 } 404 405 <-sawReady 406 407 conn.Close() 408 409 close(server1Done) 410 }() 411 412 rb := manual.NewBuilderWithScheme("whatever") 413 rb.InitialState(resolver.State{Addresses: []resolver.Address{ 414 {Addr: lis1.Addr().String()}, 415 {Addr: lis2.Addr().String()}, 416 }}) 417 client, err := DialContext(ctx, "whatever:///this-gets-overwritten", WithInsecure(), WithBalancerName(stateRecordingBalancerName), WithResolvers(rb)) 418 if err != nil { 419 t.Fatal(err) 420 } 421 defer client.Close() 422 go stayConnected(client) 423 424 stateNotifications := testBalancerBuilder.nextStateNotifier() 425 426 timeout := time.After(2 * time.Second) 427 428 for i := 0; i < len(want); i++ { 429 select { 430 case <-timeout: 431 t.Fatalf("timed out waiting for state %d (%v) in flow %v", i, want[i], want) 432 case seen := <-stateNotifications: 433 if seen == connectivity.Ready { 434 sawReady <- struct{}{} 435 } 436 if seen != want[i] { 437 t.Fatalf("expected to see %v at position %d in flow %v, got %v", want[i], i, want, seen) 438 } 439 } 440 } 441 select { 442 case <-timeout: 443 t.Fatal("saw the correct state transitions, but timed out waiting for client to finish interactions with server 1") 444 case <-server1Done: 445 } 446 } 447 448 type stateRecordingBalancer struct { 449 notifier chan<- connectivity.State 450 balancer.Balancer 451 } 452 453 func (b *stateRecordingBalancer) UpdateSubConnState(sc balancer.SubConn, s balancer.SubConnState) { 454 b.notifier <- s.ConnectivityState 455 b.Balancer.UpdateSubConnState(sc, s) 456 } 457 458 func (b *stateRecordingBalancer) ResetNotifier(r chan<- connectivity.State) { 459 b.notifier = r 460 } 461 462 func (b *stateRecordingBalancer) Close() { 463 b.Balancer.Close() 464 } 465 466 type stateRecordingBalancerBuilder struct { 467 mu sync.Mutex 468 notifier chan connectivity.State // The notifier used in the last Balancer. 469 } 470 471 func newStateRecordingBalancerBuilder() *stateRecordingBalancerBuilder { 472 return &stateRecordingBalancerBuilder{} 473 } 474 475 func (b *stateRecordingBalancerBuilder) Name() string { 476 return stateRecordingBalancerName 477 } 478 479 func (b *stateRecordingBalancerBuilder) Build(cc balancer.ClientConn, opts balancer.BuildOptions) balancer.Balancer { 480 stateNotifications := make(chan connectivity.State, 10) 481 b.mu.Lock() 482 b.notifier = stateNotifications 483 b.mu.Unlock() 484 return &stateRecordingBalancer{ 485 notifier: stateNotifications, 486 Balancer: balancer.Get(PickFirstBalancerName).Build(cc, opts), 487 } 488 } 489 490 func (b *stateRecordingBalancerBuilder) nextStateNotifier() <-chan connectivity.State { 491 b.mu.Lock() 492 defer b.mu.Unlock() 493 ret := b.notifier 494 b.notifier = nil 495 return ret 496 } 497 498 type noBackoff struct{} 499 500 func (b noBackoff) Backoff(int) time.Duration { return time.Duration(0) } 501 502 // Keep reading until something causes the connection to die (EOF, server 503 // closed, etc). Useful as a tool for mindlessly keeping the connection 504 // healthy, since the client will error if things like client prefaces are not 505 // accepted in a timely fashion. 506 func keepReading(conn net.Conn) { 507 buf := make([]byte, 1024) 508 for _, err := conn.Read(buf); err == nil; _, err = conn.Read(buf) { 509 } 510 }