google.golang.org/grpc@v1.74.2/test/clientconn_test.go (about) 1 /* 2 * 3 * Copyright 2022 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 test 20 21 import ( 22 "context" 23 "fmt" 24 "strings" 25 "testing" 26 "time" 27 28 "google.golang.org/grpc" 29 "google.golang.org/grpc/codes" 30 "google.golang.org/grpc/connectivity" 31 "google.golang.org/grpc/credentials/insecure" 32 "google.golang.org/grpc/internal" 33 "google.golang.org/grpc/internal/channelz" 34 "google.golang.org/grpc/internal/grpcsync" 35 "google.golang.org/grpc/internal/stubserver" 36 "google.golang.org/grpc/internal/testutils" 37 testgrpc "google.golang.org/grpc/interop/grpc_testing" 38 testpb "google.golang.org/grpc/interop/grpc_testing" 39 "google.golang.org/grpc/resolver" 40 "google.golang.org/grpc/resolver/manual" 41 "google.golang.org/grpc/stats" 42 "google.golang.org/grpc/status" 43 ) 44 45 // TestClientConnClose_WithPendingRPC tests the scenario where the channel has 46 // not yet received any update from the name resolver and hence RPCs are 47 // blocking. The test verifies that closing the ClientConn unblocks the RPC with 48 // the expected error code. 49 func (s) TestClientConnClose_WithPendingRPC(t *testing.T) { 50 r := manual.NewBuilderWithScheme("whatever") 51 cc, err := grpc.NewClient(r.Scheme()+":///test.server", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithResolvers(r)) 52 if err != nil { 53 t.Fatalf("grpc.NewClient() failed: %v", err) 54 } 55 client := testgrpc.NewTestServiceClient(cc) 56 57 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 58 defer cancel() 59 doneErrCh := make(chan error, 1) 60 go func() { 61 // This RPC would block until the ClientConn is closed, because the 62 // resolver has not provided its first update yet. 63 _, err := client.EmptyCall(ctx, &testpb.Empty{}) 64 if status.Code(err) != codes.Canceled || !strings.Contains(err.Error(), "client connection is closing") { 65 doneErrCh <- fmt.Errorf("EmptyCall() = %v, want %s", err, codes.Canceled) 66 } 67 doneErrCh <- nil 68 }() 69 70 // Make sure that there is one pending RPC on the ClientConn before attempting 71 // to close it. If we don't do this, cc.Close() can happen before the above 72 // goroutine gets to make the RPC. 73 for { 74 if err := ctx.Err(); err != nil { 75 t.Fatal(err) 76 } 77 tcs, _ := channelz.GetTopChannels(0, 0) 78 if len(tcs) != 1 { 79 t.Fatalf("there should only be one top channel, not %d", len(tcs)) 80 } 81 started := tcs[0].ChannelMetrics.CallsStarted.Load() 82 completed := tcs[0].ChannelMetrics.CallsSucceeded.Load() + tcs[0].ChannelMetrics.CallsFailed.Load() 83 if (started - completed) == 1 { 84 break 85 } 86 time.Sleep(defaultTestShortTimeout) 87 } 88 cc.Close() 89 if err := <-doneErrCh; err != nil { 90 t.Fatal(err) 91 } 92 } 93 94 type testStatsHandler struct { 95 nameResolutionDelayed bool 96 } 97 98 // TagRPC is called when an RPC is initiated and allows adding metadata to the 99 // context. It checks if the RPC experienced a name resolution delay and 100 // updates the handler's state. 101 func (h *testStatsHandler) TagRPC(ctx context.Context, rpcInfo *stats.RPCTagInfo) context.Context { 102 h.nameResolutionDelayed = rpcInfo.NameResolutionDelay 103 return ctx 104 } 105 106 // This method is required to satisfy the stats.Handler interface. 107 func (h *testStatsHandler) HandleRPC(_ context.Context, _ stats.RPCStats) {} 108 109 // TagConn exists to satisfy stats.Handler. 110 func (h *testStatsHandler) TagConn(ctx context.Context, _ *stats.ConnTagInfo) context.Context { 111 return ctx 112 } 113 114 // HandleConn exists to satisfy stats.Handler. 115 func (h *testStatsHandler) HandleConn(_ context.Context, _ stats.ConnStats) {} 116 117 // TestClientConnRPC_WithoutNameResolutionDelay verify that if the resolution 118 // has already happened once before at the time of making RPC, the name 119 // resolution flag is not set indicating there was no delay in name resolution. 120 func (s) TestClientConnRPC_WithoutNameResolutionDelay(t *testing.T) { 121 statsHandler := &testStatsHandler{} 122 ss := &stubserver.StubServer{ 123 EmptyCallF: func(context.Context, *testpb.Empty) (*testpb.Empty, error) { 124 return &testpb.Empty{}, nil 125 }, 126 } 127 if err := ss.Start(nil, grpc.WithStatsHandler(statsHandler)); err != nil { 128 t.Fatalf("Failed to start StubServer: %v", err) 129 } 130 defer ss.Stop() 131 132 rb := manual.NewBuilderWithScheme("instant") 133 rb.InitialState(resolver.State{Addresses: []resolver.Address{{Addr: ss.Address}}}) 134 cc := ss.CC 135 defer cc.Close() 136 137 cc.Connect() 138 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 139 defer cancel() 140 testutils.AwaitState(ctx, t, cc, connectivity.Ready) 141 client := testgrpc.NewTestServiceClient(cc) 142 // Verify that the RPC succeeds. 143 if _, err := client.EmptyCall(ctx, &testpb.Empty{}); err != nil { 144 t.Fatalf("First RPC failed unexpectedly: %v", err) 145 } 146 // verifying that RPC was not blocked on resolver indicating there was no 147 // delay in name resolution. 148 if statsHandler.nameResolutionDelayed { 149 t.Fatalf("statsHandler.nameResolutionDelayed = %v; want false", statsHandler.nameResolutionDelayed) 150 } 151 } 152 153 // TestStatsHandlerDetectsResolutionDelay verifies that if this is the 154 // first time resolution is happening at the time of making RPC, 155 // nameResolutionDelayed flag is set indicating there was a delay in name 156 // resolution waiting for resolver to return addresses. 157 func (s) TestClientConnRPC_WithNameResolutionDelay(t *testing.T) { 158 resolutionWait := grpcsync.NewEvent() 159 prevHook := internal.NewStreamWaitingForResolver 160 internal.NewStreamWaitingForResolver = func() { resolutionWait.Fire() } 161 defer func() { internal.NewStreamWaitingForResolver = prevHook }() 162 163 ss := &stubserver.StubServer{ 164 EmptyCallF: func(context.Context, *testpb.Empty) (*testpb.Empty, error) { 165 return &testpb.Empty{}, nil 166 }, 167 } 168 if err := ss.Start(nil); err != nil { 169 t.Fatalf("Failed to start StubServer: %v", err) 170 } 171 defer ss.Stop() 172 173 statsHandler := &testStatsHandler{} 174 rb := manual.NewBuilderWithScheme("delayed") 175 cc, err := grpc.NewClient(rb.Scheme()+":///test.server", 176 grpc.WithTransportCredentials(insecure.NewCredentials()), 177 grpc.WithResolvers(rb), 178 grpc.WithStatsHandler(statsHandler), 179 ) 180 if err != nil { 181 t.Fatalf("grpc.NewClient() failed: %v", err) 182 } 183 defer cc.Close() 184 go func() { 185 <-resolutionWait.Done() 186 rb.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: ss.Address}}}) 187 }() 188 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 189 defer cancel() 190 client := testgrpc.NewTestServiceClient(cc) 191 if _, err := client.EmptyCall(ctx, &testpb.Empty{}); err != nil { 192 t.Fatalf("EmptyCall RPC failed: %v", err) 193 } 194 if !statsHandler.nameResolutionDelayed { 195 t.Fatalf("statsHandler.nameResolutionDelayed = %v; want true", statsHandler.nameResolutionDelayed) 196 } 197 }