gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/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  	"gitee.com/ks-custle/core-gm/grpc/balancer"
    29  	"gitee.com/ks-custle/core-gm/grpc/connectivity"
    30  	"gitee.com/ks-custle/core-gm/grpc/internal/testutils"
    31  	"gitee.com/ks-custle/core-gm/grpc/resolver"
    32  	"gitee.com/ks-custle/core-gm/grpc/resolver/manual"
    33  	"gitee.com/ks-custle/core-gm/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  }