google.golang.org/grpc@v1.72.2/test/roundrobin_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 "strings" 24 "testing" 25 "time" 26 27 "google.golang.org/grpc" 28 "google.golang.org/grpc/codes" 29 "google.golang.org/grpc/connectivity" 30 "google.golang.org/grpc/credentials/insecure" 31 "google.golang.org/grpc/internal/channelz" 32 imetadata "google.golang.org/grpc/internal/metadata" 33 "google.golang.org/grpc/internal/stubserver" 34 "google.golang.org/grpc/internal/testutils" 35 rrutil "google.golang.org/grpc/internal/testutils/roundrobin" 36 "google.golang.org/grpc/metadata" 37 "google.golang.org/grpc/resolver" 38 "google.golang.org/grpc/resolver/manual" 39 "google.golang.org/grpc/status" 40 41 testgrpc "google.golang.org/grpc/interop/grpc_testing" 42 testpb "google.golang.org/grpc/interop/grpc_testing" 43 ) 44 45 const rrServiceConfig = `{"loadBalancingConfig": [{"round_robin":{}}]}` 46 47 func testRoundRobinBasic(ctx context.Context, t *testing.T, opts ...grpc.DialOption) (*grpc.ClientConn, *manual.Resolver, []*stubserver.StubServer) { 48 t.Helper() 49 50 r := manual.NewBuilderWithScheme("whatever") 51 52 const backendCount = 5 53 backends := make([]*stubserver.StubServer, backendCount) 54 endpoints := make([]resolver.Endpoint, backendCount) 55 addrs := make([]resolver.Address, backendCount) 56 for i := 0; i < backendCount; i++ { 57 backend := &stubserver.StubServer{ 58 EmptyCallF: func(context.Context, *testpb.Empty) (*testpb.Empty, error) { return &testpb.Empty{}, nil }, 59 } 60 if err := backend.StartServer(); err != nil { 61 t.Fatalf("Failed to start backend: %v", err) 62 } 63 t.Logf("Started TestService backend at: %q", backend.Address) 64 t.Cleanup(func() { backend.Stop() }) 65 66 backends[i] = backend 67 addrs[i] = resolver.Address{Addr: backend.Address} 68 endpoints[i] = resolver.Endpoint{Addresses: []resolver.Address{addrs[i]}} 69 } 70 71 dopts := []grpc.DialOption{ 72 grpc.WithTransportCredentials(insecure.NewCredentials()), 73 grpc.WithResolvers(r), 74 grpc.WithDefaultServiceConfig(rrServiceConfig), 75 } 76 dopts = append(dopts, opts...) 77 cc, err := grpc.NewClient(r.Scheme()+":///test.server", dopts...) 78 if err != nil { 79 t.Fatalf("grpc.NewClient() failed: %v", err) 80 } 81 t.Cleanup(func() { cc.Close() }) 82 client := testgrpc.NewTestServiceClient(cc) 83 84 // At this point, the resolver has not returned any addresses to the channel. 85 // This RPC must block until the context expires. 86 sCtx, sCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout) 87 defer sCancel() 88 if _, err := client.EmptyCall(sCtx, &testpb.Empty{}); status.Code(err) != codes.DeadlineExceeded { 89 t.Fatalf("EmptyCall() = %s, want %s", status.Code(err), codes.DeadlineExceeded) 90 } 91 92 r.UpdateState(resolver.State{Addresses: addrs}) 93 if err := rrutil.CheckRoundRobinRPCs(ctx, client, addrs); err != nil { 94 t.Fatal(err) 95 } 96 return cc, r, backends 97 } 98 99 // TestRoundRobin_Basic tests the most basic scenario for round_robin. It brings 100 // up a bunch of backends and verifies that RPCs are getting round robin-ed 101 // across these backends. 102 func (s) TestRoundRobin_Basic(t *testing.T) { 103 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 104 defer cancel() 105 testRoundRobinBasic(ctx, t) 106 } 107 108 // TestRoundRobin_AddressesRemoved tests the scenario where a bunch of backends 109 // are brought up, and round_robin is configured as the LB policy and RPCs are 110 // being correctly round robin-ed across these backends. We then send a resolver 111 // update with no addresses and verify that the channel enters TransientFailure 112 // and RPCs fail with an expected error message. 113 func (s) TestRoundRobin_AddressesRemoved(t *testing.T) { 114 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 115 defer cancel() 116 cc, r, _ := testRoundRobinBasic(ctx, t) 117 118 // Send a resolver update with no addresses. This should push the channel into 119 // TransientFailure. 120 r.UpdateState(resolver.State{Endpoints: []resolver.Endpoint{}}) 121 testutils.AwaitState(ctx, t, cc, connectivity.TransientFailure) 122 123 const msgWant = "no children to pick from" 124 client := testgrpc.NewTestServiceClient(cc) 125 if _, err := client.EmptyCall(ctx, &testpb.Empty{}); !strings.Contains(status.Convert(err).Message(), msgWant) { 126 t.Fatalf("EmptyCall() = %v, want Contains(Message(), %q)", err, msgWant) 127 } 128 } 129 130 // TestRoundRobin_NewAddressWhileBlocking tests the case where round_robin is 131 // configured on a channel, things are working as expected and then a resolver 132 // updates removes all addresses. An RPC attempted at this point in time will be 133 // blocked because there are no valid ¡ds. This test verifies that when new 134 // backends are added, the RPC is able to complete. 135 func (s) TestRoundRobin_NewAddressWhileBlocking(t *testing.T) { 136 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 137 defer cancel() 138 cc, r, backends := testRoundRobinBasic(ctx, t) 139 140 // Send a resolver update with no addresses. This should push the channel into 141 // TransientFailure. 142 r.UpdateState(resolver.State{Addresses: []resolver.Address{}}) 143 testutils.AwaitState(ctx, t, cc, connectivity.TransientFailure) 144 145 client := testgrpc.NewTestServiceClient(cc) 146 doneCh := make(chan struct{}) 147 go func() { 148 // The channel is currently in TransientFailure and this RPC will block 149 // until the channel becomes Ready, which will only happen when we push a 150 // resolver update with a valid backend address. 151 if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); err != nil { 152 t.Errorf("EmptyCall() = %v, want <nil>", err) 153 } 154 close(doneCh) 155 }() 156 157 // Make sure that there is one pending RPC on the ClientConn before attempting 158 // to push new addresses through the name resolver. If we don't do this, the 159 // resolver update can happen before the above goroutine gets to make the RPC. 160 for { 161 if err := ctx.Err(); err != nil { 162 t.Fatal(err) 163 } 164 tcs, _ := channelz.GetTopChannels(0, 0) 165 if len(tcs) != 1 { 166 t.Fatalf("there should only be one top channel, not %d", len(tcs)) 167 } 168 started := tcs[0].ChannelMetrics.CallsStarted.Load() 169 completed := tcs[0].ChannelMetrics.CallsSucceeded.Load() + tcs[0].ChannelMetrics.CallsFailed.Load() 170 if (started - completed) == 1 { 171 break 172 } 173 time.Sleep(defaultTestShortTimeout) 174 } 175 176 // Send a resolver update with a valid backend to push the channel to Ready 177 // and unblock the above RPC. 178 r.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: backends[0].Address}}}) 179 180 select { 181 case <-ctx.Done(): 182 t.Fatal("Timeout when waiting for blocked RPC to complete") 183 case <-doneCh: 184 } 185 } 186 187 // TestRoundRobin_OneServerDown tests the scenario where a channel is configured 188 // to round robin across a set of backends, and things are working correctly. 189 // One backend goes down. The test verifies that going forward, RPCs are round 190 // robin-ed across the remaining set of backends. 191 func (s) TestRoundRobin_OneServerDown(t *testing.T) { 192 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 193 defer cancel() 194 cc, _, backends := testRoundRobinBasic(ctx, t) 195 196 // Stop one backend. RPCs should round robin across the remaining backends. 197 backends[len(backends)-1].Stop() 198 199 addrs := make([]resolver.Address, len(backends)-1) 200 for i := 0; i < len(backends)-1; i++ { 201 addrs[i] = resolver.Address{Addr: backends[i].Address} 202 } 203 client := testgrpc.NewTestServiceClient(cc) 204 if err := rrutil.CheckRoundRobinRPCs(ctx, client, addrs); err != nil { 205 t.Fatalf("RPCs are not being round robined across remaining servers: %v", err) 206 } 207 } 208 209 // TestRoundRobin_AllServersDown tests the scenario where a channel is 210 // configured to round robin across a set of backends, and things are working 211 // correctly. Then, all backends go down. The test verifies that the channel 212 // moves to TransientFailure and failfast RPCs fail with Unavailable. 213 func (s) TestRoundRobin_AllServersDown(t *testing.T) { 214 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 215 defer cancel() 216 cc, _, backends := testRoundRobinBasic(ctx, t) 217 218 // Stop all backends. 219 for _, b := range backends { 220 b.Stop() 221 } 222 223 testutils.AwaitState(ctx, t, cc, connectivity.TransientFailure) 224 225 // Failfast RPCs should fail with Unavailable. 226 client := testgrpc.NewTestServiceClient(cc) 227 228 if _, err := client.EmptyCall(ctx, &testpb.Empty{}); status.Code(err) != codes.Unavailable { 229 t.Fatalf("EmptyCall got err: %v; want Unavailable", err) 230 } 231 } 232 233 // TestRoundRobin_UpdateAddressAttributes tests the scenario where the addresses 234 // returned by the resolver contain attributes. The test verifies that the 235 // attributes contained in the addresses show up as RPC metadata in the backend. 236 func (s) TestRoundRobin_UpdateAddressAttributes(t *testing.T) { 237 const ( 238 testMDKey = "test-md" 239 testMDValue = "test-md-value" 240 ) 241 r := manual.NewBuilderWithScheme("whatever") 242 243 // Spin up a StubServer to serve as a backend. The implementation verifies 244 // that the expected metadata is received. 245 testMDChan := make(chan []string, 1) 246 backend := &stubserver.StubServer{ 247 EmptyCallF: func(ctx context.Context, _ *testpb.Empty) (*testpb.Empty, error) { 248 md, ok := metadata.FromIncomingContext(ctx) 249 if ok { 250 select { 251 case testMDChan <- md[testMDKey]: 252 case <-ctx.Done(): 253 return nil, ctx.Err() 254 } 255 } 256 return &testpb.Empty{}, nil 257 }, 258 } 259 if err := backend.StartServer(); err != nil { 260 t.Fatalf("Failed to start backend: %v", err) 261 } 262 t.Logf("Started TestService backend at: %q", backend.Address) 263 t.Cleanup(func() { backend.Stop() }) 264 265 // Dial the backend with round_robin as the LB policy. 266 dopts := []grpc.DialOption{ 267 grpc.WithTransportCredentials(insecure.NewCredentials()), 268 grpc.WithResolvers(r), 269 grpc.WithDefaultServiceConfig(rrServiceConfig), 270 } 271 // Set an initial resolver update with no address attributes. 272 addr := resolver.Address{Addr: backend.Address} 273 r.InitialState(resolver.State{Addresses: []resolver.Address{addr}}) 274 cc, err := grpc.NewClient(r.Scheme()+":///test.server", dopts...) 275 if err != nil { 276 t.Fatalf("grpc.NewClient() failed: %v", err) 277 } 278 t.Cleanup(func() { cc.Close() }) 279 280 // Make an RPC and ensure it does not contain the metadata we are looking for. 281 client := testgrpc.NewTestServiceClient(cc) 282 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 283 defer cancel() 284 if _, err := client.EmptyCall(ctx, &testpb.Empty{}); err != nil { 285 t.Fatalf("EmptyCall() = %v, want <nil>", err) 286 } 287 select { 288 case <-ctx.Done(): 289 t.Fatalf("Timeout when waiting for metadata received in RPC") 290 case md := <-testMDChan: 291 if len(md) != 0 { 292 t.Fatalf("received metadata %v, want nil", md) 293 } 294 } 295 296 // Send a resolver update with address attributes. 297 addrWithAttributes := imetadata.Set(addr, metadata.Pairs(testMDKey, testMDValue)) 298 r.UpdateState(resolver.State{Addresses: []resolver.Address{addrWithAttributes}}) 299 300 // Make an RPC and ensure it contains the metadata we are looking for. The 301 // resolver update isn't processed synchronously, so we wait some time before 302 // failing if some RPCs do not contain it. 303 Done: 304 for { 305 if _, err := client.EmptyCall(ctx, &testpb.Empty{}); err != nil { 306 t.Fatalf("EmptyCall() = %v, want <nil>", err) 307 } 308 select { 309 case <-ctx.Done(): 310 t.Fatalf("Timeout when waiting for metadata received in RPC") 311 case md := <-testMDChan: 312 if len(md) == 1 && md[0] == testMDValue { 313 break Done 314 } 315 } 316 time.Sleep(defaultTestShortTimeout) 317 } 318 }