@@ -21,23 +21,27 @@ package endpointsharding_test
21
21
import (
22
22
"context"
23
23
"encoding/json"
24
+ "errors"
24
25
"fmt"
25
26
"log"
26
27
"strings"
27
28
"testing"
28
29
"time"
29
30
30
31
"google.golang.org/grpc"
32
+ "google.golang.org/grpc/backoff"
31
33
"google.golang.org/grpc/balancer"
32
34
"google.golang.org/grpc/balancer/endpointsharding"
33
35
"google.golang.org/grpc/balancer/pickfirst/pickfirstleaf"
34
36
"google.golang.org/grpc/codes"
37
+ "google.golang.org/grpc/connectivity"
35
38
"google.golang.org/grpc/credentials/insecure"
36
39
"google.golang.org/grpc/grpclog"
37
40
"google.golang.org/grpc/internal"
38
41
"google.golang.org/grpc/internal/balancer/stub"
39
42
"google.golang.org/grpc/internal/grpctest"
40
43
"google.golang.org/grpc/internal/stubserver"
44
+ "google.golang.org/grpc/internal/testutils"
41
45
"google.golang.org/grpc/internal/testutils/roundrobin"
42
46
"google.golang.org/grpc/peer"
43
47
"google.golang.org/grpc/resolver"
@@ -125,7 +129,9 @@ func (fp *fakePetiole) UpdateState(state balancer.State) {
125
129
// special picker, so it should fallback to the default behavior, which is to
126
130
// round_robin amongst the endpoint children that are in the aggregated state.
127
131
// It also verifies the petiole has access to the raw child state in case it
128
- // wants to implement a custom picker.
132
+ // wants to implement a custom picker. The test sends a resolver error to the
133
+ // endpointsharding balancer and verifies an error picker from the children
134
+ // is used while making an RPC.
129
135
func (s ) TestEndpointShardingBasic (t * testing.T ) {
130
136
backend1 := stubserver .StartTestService (t , nil )
131
137
defer backend1 .Stop ()
@@ -135,7 +141,7 @@ func (s) TestEndpointShardingBasic(t *testing.T) {
135
141
mr := manual .NewBuilderWithScheme ("e2e-test" )
136
142
defer mr .Close ()
137
143
138
- json := `{"loadBalancingConfig": [{"fake_petiole ":{}}]}`
144
+ json := fmt . Sprintf ( `{"loadBalancingConfig": [{"%s ":{}}]}` , fakePetioleName )
139
145
sc := internal .ParseServiceConfig .(func (string ) * serviceconfig.ParseResult )(json )
140
146
mr .InitialState (resolver.State {
141
147
Endpoints : []resolver.Endpoint {
@@ -145,7 +151,20 @@ func (s) TestEndpointShardingBasic(t *testing.T) {
145
151
ServiceConfig : sc ,
146
152
})
147
153
148
- cc , err := grpc .NewClient (mr .Scheme ()+ ":///" , grpc .WithResolvers (mr ), grpc .WithTransportCredentials (insecure .NewCredentials ()))
154
+ dOpts := []grpc.DialOption {
155
+ grpc .WithResolvers (mr ), grpc .WithTransportCredentials (insecure .NewCredentials ()),
156
+ // Use a large backoff delay to avoid the error picker being updated
157
+ // too quickly.
158
+ grpc .WithConnectParams (grpc.ConnectParams {
159
+ Backoff : backoff.Config {
160
+ BaseDelay : 2 * defaultTestTimeout ,
161
+ Multiplier : float64 (0 ),
162
+ Jitter : float64 (0 ),
163
+ MaxDelay : 2 * defaultTestTimeout ,
164
+ },
165
+ }),
166
+ }
167
+ cc , err := grpc .NewClient (mr .Scheme ()+ ":///" , dOpts ... )
149
168
if err != nil {
150
169
log .Fatalf ("Failed to create new client: %v" , err )
151
170
}
@@ -159,6 +178,29 @@ func (s) TestEndpointShardingBasic(t *testing.T) {
159
178
if err = roundrobin .CheckRoundRobinRPCs (ctx , client , []resolver.Address {{Addr : backend1 .Address }, {Addr : backend2 .Address }}); err != nil {
160
179
t .Fatalf ("error in expected round robin: %v" , err )
161
180
}
181
+
182
+ // Stopping both the backends should make the channel enter
183
+ // TransientFailure.
184
+ backend1 .Stop ()
185
+ backend2 .Stop ()
186
+ testutils .AwaitState (ctx , t , cc , connectivity .TransientFailure )
187
+
188
+ // When the resolver reports an error, the picker should get updated to
189
+ // return the resolver error.
190
+ mr .ReportError (errors .New ("test error" ))
191
+ testutils .AwaitState (ctx , t , cc , connectivity .TransientFailure )
192
+ for ; ctx .Err () == nil ; <- time .After (time .Millisecond ) {
193
+ _ , err := client .EmptyCall (ctx , & testpb.Empty {})
194
+ if err == nil {
195
+ t .Fatalf ("EmptyCall succeeded when expected to fail with %q" , "test error" )
196
+ }
197
+ if strings .Contains (err .Error (), "test error" ) {
198
+ break
199
+ }
200
+ }
201
+ if ctx .Err () != nil {
202
+ t .Fatalf ("Context timed out waiting for picker with resolver error." )
203
+ }
162
204
}
163
205
164
206
// Tests that endpointsharding doesn't automatically re-connect IDLE children.
0 commit comments