@@ -27,12 +27,13 @@ import (
27
27
"time"
28
28
29
29
"github.com/google/go-cmp/cmp"
30
-
31
30
"google.golang.org/grpc"
31
+ "google.golang.org/grpc/connectivity"
32
32
"google.golang.org/grpc/credentials/insecure"
33
33
"google.golang.org/grpc/internal"
34
34
"google.golang.org/grpc/internal/grpctest"
35
35
"google.golang.org/grpc/internal/stubserver"
36
+ "google.golang.org/grpc/internal/testutils"
36
37
testgrpc "google.golang.org/grpc/interop/grpc_testing"
37
38
testpb "google.golang.org/grpc/interop/grpc_testing"
38
39
"google.golang.org/grpc/peer"
@@ -42,7 +43,8 @@ import (
42
43
)
43
44
44
45
const (
45
- defaultTestTimeout = 5 * time .Second
46
+ defaultTestTimeout = 5 * time .Second
47
+ defaultTestShortTimeout = 10 * time .Millisecond
46
48
)
47
49
48
50
type s struct {
@@ -706,3 +708,63 @@ func (s) TestLeastRequestEndpoints_MultipleAddresses(t *testing.T) {
706
708
t .Fatalf ("error in expected round robin: %v" , err )
707
709
}
708
710
}
711
+
712
+ // Test tests that the least request balancer properly surfaces resolver
713
+ // errors.
714
+ func (s ) TestLeastRequestEndpoints_ResolverError (t * testing.T ) {
715
+ const sc = `{"loadBalancingConfig": [{"least_request_experimental": {}}]}`
716
+ mr := manual .NewBuilderWithScheme ("lr-e2e" )
717
+ defer mr .Close ()
718
+
719
+ cc , err := grpc .NewClient (
720
+ mr .Scheme ()+ ":///" ,
721
+ grpc .WithResolvers (mr ),
722
+ grpc .WithTransportCredentials (insecure .NewCredentials ()),
723
+ grpc .WithDefaultServiceConfig (sc ),
724
+ )
725
+ if err != nil {
726
+ t .Fatalf ("grpc.NewClient() failed: %v" , err )
727
+ }
728
+ defer cc .Close ()
729
+
730
+ // We need to pass an endpoint with a valid address to the resolver before
731
+ // reporting an error - otherwise endpointsharding does not report the
732
+ // error through.
733
+ lis , err := testutils .LocalTCPListener ()
734
+ if err != nil {
735
+ t .Fatalf ("net.Listen() failed: %v" , err )
736
+ }
737
+ // Act like a server that closes the connection without sending a server
738
+ // preface.
739
+ go func () {
740
+ conn , err := lis .Accept ()
741
+ if err != nil {
742
+ t .Errorf ("Unexpected error when accepting a connection: %v" , err )
743
+ }
744
+ conn .Close ()
745
+ }()
746
+ mr .UpdateState (resolver.State {
747
+ Endpoints : []resolver.Endpoint {{Addresses : []resolver.Address {{Addr : lis .Addr ().String ()}}}},
748
+ })
749
+ cc .Connect ()
750
+
751
+ ctx , cancel := context .WithTimeout (context .Background (), defaultTestTimeout )
752
+ defer cancel ()
753
+ testutils .AwaitState (ctx , t , cc , connectivity .TransientFailure )
754
+
755
+ // Report an error through the resolver
756
+ resolverErr := fmt .Errorf ("simulated resolver error" )
757
+ mr .CC ().ReportError (resolverErr )
758
+
759
+ // Ensure the client returns the expected resolver error.
760
+ testServiceClient := testgrpc .NewTestServiceClient (cc )
761
+ for ; ctx .Err () == nil ; <- time .After (defaultTestShortTimeout ) {
762
+ _ , err = testServiceClient .EmptyCall (ctx , & testpb.Empty {})
763
+ if strings .Contains (err .Error (), resolverErr .Error ()) {
764
+ break
765
+ }
766
+ }
767
+ if ctx .Err () != nil {
768
+ t .Fatalf ("Timeout when waiting for RPCs to fail with error containing %s. Last error: %v" , resolverErr , err )
769
+ }
770
+ }
0 commit comments