use of io.grpc.lb.v1.LoadBalanceRequest in project grpc-java by grpc.
the class GrpclbLoadBalancerTest method loadReporting.
@Test
public void loadReporting() {
Metadata headers = new Metadata();
PickSubchannelArgs args = mock(PickSubchannelArgs.class);
when(args.getHeaders()).thenReturn(headers);
long loadReportIntervalMillis = 1983;
List<EquivalentAddressGroup> grpclbBalancerList = createResolvedBalancerAddresses(1);
deliverResolvedAddresses(Collections.<EquivalentAddressGroup>emptyList(), grpclbBalancerList);
// Fallback timer is started as soon as address is resolved.
assertEquals(1, fakeClock.numPendingTasks(FALLBACK_MODE_TASK_FILTER));
assertEquals(1, fakeOobChannels.size());
verify(mockLbService).balanceLoad(lbResponseObserverCaptor.capture());
StreamObserver<LoadBalanceResponse> lbResponseObserver = lbResponseObserverCaptor.getValue();
assertEquals(1, lbRequestObservers.size());
StreamObserver<LoadBalanceRequest> lbRequestObserver = lbRequestObservers.poll();
InOrder inOrder = inOrder(lbRequestObserver);
InOrder helperInOrder = inOrder(helper, subchannelPool);
inOrder.verify(lbRequestObserver).onNext(eq(LoadBalanceRequest.newBuilder().setInitialRequest(InitialLoadBalanceRequest.newBuilder().setName(SERVICE_AUTHORITY).build()).build()));
// Simulate receiving LB response
assertEquals(0, fakeClock.numPendingTasks(LOAD_REPORTING_TASK_FILTER));
lbResponseObserver.onNext(buildInitialResponse(loadReportIntervalMillis));
// Load reporting task is scheduled
assertEquals(1, fakeClock.numPendingTasks(LOAD_REPORTING_TASK_FILTER));
assertEquals(0, fakeClock.runDueTasks());
List<ServerEntry> backends = Arrays.asList(new ServerEntry("127.0.0.1", 2000, "token0001"), // drop
new ServerEntry("token0001"), new ServerEntry("127.0.0.1", 2010, "token0002"), // drop
new ServerEntry("token0003"));
lbResponseObserver.onNext(buildLbResponse(backends));
assertEquals(2, mockSubchannels.size());
Subchannel subchannel1 = mockSubchannels.poll();
Subchannel subchannel2 = mockSubchannels.poll();
deliverSubchannelState(subchannel1, ConnectivityStateInfo.forNonError(CONNECTING));
deliverSubchannelState(subchannel2, ConnectivityStateInfo.forNonError(CONNECTING));
deliverSubchannelState(subchannel1, ConnectivityStateInfo.forNonError(READY));
deliverSubchannelState(subchannel2, ConnectivityStateInfo.forNonError(READY));
helperInOrder.verify(helper, atLeast(1)).updateBalancingState(eq(READY), pickerCaptor.capture());
RoundRobinPicker picker = (RoundRobinPicker) pickerCaptor.getValue();
assertThat(picker.dropList).containsExactly(null, new DropEntry(getLoadRecorder(), "token0001"), null, new DropEntry(getLoadRecorder(), "token0003")).inOrder();
assertThat(picker.pickList).containsExactly(new BackendEntry(subchannel1, getLoadRecorder(), "token0001"), new BackendEntry(subchannel2, getLoadRecorder(), "token0002")).inOrder();
// Report, no data
assertNextReport(inOrder, lbRequestObserver, loadReportIntervalMillis, ClientStats.newBuilder().build());
PickResult pick1 = picker.pickSubchannel(args);
assertSame(subchannel1, pick1.getSubchannel());
assertSame(getLoadRecorder(), pick1.getStreamTracerFactory());
// Merely the pick will not be recorded as upstart.
assertNextReport(inOrder, lbRequestObserver, loadReportIntervalMillis, ClientStats.newBuilder().build());
ClientStreamTracer tracer1 = pick1.getStreamTracerFactory().newClientStreamTracer(STREAM_INFO, new Metadata());
tracer1.streamCreated(Attributes.EMPTY, new Metadata());
PickResult pick2 = picker.pickSubchannel(args);
assertNull(pick2.getSubchannel());
assertSame(DROP_PICK_RESULT, pick2);
// Report includes upstart of pick1 and the drop of pick2
assertNextReport(inOrder, lbRequestObserver, loadReportIntervalMillis, ClientStats.newBuilder().setNumCallsStarted(2).setNumCallsFinished(// pick2
1).addCallsFinishedWithDrop(ClientStatsPerToken.newBuilder().setLoadBalanceToken("token0001").setNumCalls(// pick2
1).build()).build());
PickResult pick3 = picker.pickSubchannel(args);
assertSame(subchannel2, pick3.getSubchannel());
assertSame(getLoadRecorder(), pick3.getStreamTracerFactory());
ClientStreamTracer tracer3 = pick3.getStreamTracerFactory().newClientStreamTracer(STREAM_INFO, new Metadata());
tracer3.streamCreated(Attributes.EMPTY, new Metadata());
// pick3 has sent out headers
tracer3.outboundHeaders();
// 3rd report includes pick3's upstart
assertNextReport(inOrder, lbRequestObserver, loadReportIntervalMillis, ClientStats.newBuilder().setNumCallsStarted(1).build());
PickResult pick4 = picker.pickSubchannel(args);
assertNull(pick4.getSubchannel());
assertSame(DROP_PICK_RESULT, pick4);
// pick1 ended without sending anything
tracer1.streamClosed(Status.CANCELLED);
// 4th report includes end of pick1 and drop of pick4
assertNextReport(inOrder, lbRequestObserver, loadReportIntervalMillis, ClientStats.newBuilder().setNumCallsStarted(// pick4
1).setNumCallsFinished(2).setNumCallsFinishedWithClientFailedToSend(// pick1
1).addCallsFinishedWithDrop(ClientStatsPerToken.newBuilder().setLoadBalanceToken("token0003").setNumCalls(// pick4
1).build()).build());
PickResult pick5 = picker.pickSubchannel(args);
assertSame(subchannel1, pick1.getSubchannel());
assertSame(getLoadRecorder(), pick5.getStreamTracerFactory());
ClientStreamTracer tracer5 = pick5.getStreamTracerFactory().newClientStreamTracer(STREAM_INFO, new Metadata());
tracer5.streamCreated(Attributes.EMPTY, new Metadata());
// pick3 ended without receiving response headers
tracer3.streamClosed(Status.DEADLINE_EXCEEDED);
// pick5 sent and received headers
tracer5.outboundHeaders();
tracer5.inboundHeaders();
// 5th report includes pick3's end and pick5's upstart
assertNextReport(inOrder, lbRequestObserver, loadReportIntervalMillis, ClientStats.newBuilder().setNumCallsStarted(// pick5
1).setNumCallsFinished(// pick3
1).build());
// pick5 ends
tracer5.streamClosed(Status.OK);
// 6th report includes pick5's end
assertNextReport(inOrder, lbRequestObserver, loadReportIntervalMillis, ClientStats.newBuilder().setNumCallsFinished(1).setNumCallsFinishedKnownReceived(1).build());
assertEquals(1, fakeClock.numPendingTasks());
// Balancer closes the stream, scheduled reporting task cancelled
lbResponseObserver.onError(Status.UNAVAILABLE.asException());
assertEquals(0, fakeClock.numPendingTasks());
// New stream created
verify(mockLbService, times(2)).balanceLoad(lbResponseObserverCaptor.capture());
lbResponseObserver = lbResponseObserverCaptor.getValue();
assertEquals(1, lbRequestObservers.size());
lbRequestObserver = lbRequestObservers.poll();
inOrder = inOrder(lbRequestObserver);
inOrder.verify(lbRequestObserver).onNext(eq(LoadBalanceRequest.newBuilder().setInitialRequest(InitialLoadBalanceRequest.newBuilder().setName(SERVICE_AUTHORITY).build()).build()));
// Load reporting is also requested
lbResponseObserver.onNext(buildInitialResponse(loadReportIntervalMillis));
// No picker created because balancer is still using the results from the last stream
helperInOrder.verify(helper, never()).updateBalancingState(any(ConnectivityState.class), any(SubchannelPicker.class));
// Make a new pick on that picker. It will not show up on the report of the new stream, because
// that picker is associated with the previous stream.
PickResult pick6 = picker.pickSubchannel(args);
assertNull(pick6.getSubchannel());
assertSame(DROP_PICK_RESULT, pick6);
assertNextReport(inOrder, lbRequestObserver, loadReportIntervalMillis, ClientStats.newBuilder().build());
// New stream got the list update
lbResponseObserver.onNext(buildLbResponse(backends));
// Same backends, thus no new subchannels
helperInOrder.verify(subchannelPool, never()).takeOrCreateSubchannel(any(EquivalentAddressGroup.class), any(Attributes.class));
// But the new RoundRobinEntries have a new loadRecorder, thus considered different from
// the previous list, thus a new picker is created
helperInOrder.verify(helper).updateBalancingState(eq(READY), pickerCaptor.capture());
picker = (RoundRobinPicker) pickerCaptor.getValue();
PickResult pick1p = picker.pickSubchannel(args);
assertSame(subchannel1, pick1p.getSubchannel());
assertSame(getLoadRecorder(), pick1p.getStreamTracerFactory());
pick1p.getStreamTracerFactory().newClientStreamTracer(STREAM_INFO, new Metadata());
// The pick from the new stream will be included in the report
assertNextReport(inOrder, lbRequestObserver, loadReportIntervalMillis, ClientStats.newBuilder().setNumCallsStarted(1).build());
verify(args, atLeast(0)).getHeaders();
verifyNoMoreInteractions(args);
}
use of io.grpc.lb.v1.LoadBalanceRequest in project grpc-java by grpc.
the class GrpclbLoadBalancerTest method grpclbBalancerStreamClosedAndRetried.
@Test
public void grpclbBalancerStreamClosedAndRetried() throws Exception {
LoadBalanceRequest expectedInitialRequest = LoadBalanceRequest.newBuilder().setInitialRequest(InitialLoadBalanceRequest.newBuilder().setName(SERVICE_AUTHORITY).build()).build();
InOrder inOrder = inOrder(mockLbService, backoffPolicyProvider, backoffPolicy1, backoffPolicy2, helper);
List<EquivalentAddressGroup> grpclbBalancerList = createResolvedBalancerAddresses(1);
deliverResolvedAddresses(Collections.<EquivalentAddressGroup>emptyList(), grpclbBalancerList);
assertEquals(1, fakeOobChannels.size());
@SuppressWarnings("unused") ManagedChannel oobChannel = fakeOobChannels.poll();
// First balancer RPC
inOrder.verify(mockLbService).balanceLoad(lbResponseObserverCaptor.capture());
StreamObserver<LoadBalanceResponse> lbResponseObserver = lbResponseObserverCaptor.getValue();
assertEquals(1, lbRequestObservers.size());
StreamObserver<LoadBalanceRequest> lbRequestObserver = lbRequestObservers.poll();
verify(lbRequestObserver).onNext(eq(expectedInitialRequest));
assertEquals(0, fakeClock.numPendingTasks(LB_RPC_RETRY_TASK_FILTER));
// Balancer closes it immediately (erroneously)
lbResponseObserver.onCompleted();
// Will start backoff sequence 1 (10ns)
inOrder.verify(backoffPolicyProvider).get();
inOrder.verify(backoffPolicy1).nextBackoffNanos();
assertEquals(1, fakeClock.numPendingTasks(LB_RPC_RETRY_TASK_FILTER));
inOrder.verify(helper).refreshNameResolution();
// Fast-forward to a moment before the retry
fakeClock.forwardNanos(9);
verifyNoMoreInteractions(mockLbService);
// Then time for retry
fakeClock.forwardNanos(1);
inOrder.verify(mockLbService).balanceLoad(lbResponseObserverCaptor.capture());
lbResponseObserver = lbResponseObserverCaptor.getValue();
assertEquals(1, lbRequestObservers.size());
lbRequestObserver = lbRequestObservers.poll();
verify(lbRequestObserver).onNext(eq(expectedInitialRequest));
assertEquals(0, fakeClock.numPendingTasks(LB_RPC_RETRY_TASK_FILTER));
// Balancer closes it with an error.
lbResponseObserver.onError(Status.UNAVAILABLE.asException());
// Will continue the backoff sequence 1 (100ns)
verifyNoMoreInteractions(backoffPolicyProvider);
inOrder.verify(backoffPolicy1).nextBackoffNanos();
assertEquals(1, fakeClock.numPendingTasks(LB_RPC_RETRY_TASK_FILTER));
inOrder.verify(helper).refreshNameResolution();
// Fast-forward to a moment before the retry
fakeClock.forwardNanos(100 - 1);
verifyNoMoreInteractions(mockLbService);
// Then time for retry
fakeClock.forwardNanos(1);
inOrder.verify(mockLbService).balanceLoad(lbResponseObserverCaptor.capture());
lbResponseObserver = lbResponseObserverCaptor.getValue();
assertEquals(1, lbRequestObservers.size());
lbRequestObserver = lbRequestObservers.poll();
verify(lbRequestObserver).onNext(eq(expectedInitialRequest));
assertEquals(0, fakeClock.numPendingTasks(LB_RPC_RETRY_TASK_FILTER));
// Balancer sends initial response.
lbResponseObserver.onNext(buildInitialResponse());
// Then breaks the RPC
lbResponseObserver.onError(Status.UNAVAILABLE.asException());
// Will reset the retry sequence and retry immediately, because balancer has responded.
inOrder.verify(backoffPolicyProvider).get();
inOrder.verify(mockLbService).balanceLoad(lbResponseObserverCaptor.capture());
lbResponseObserver = lbResponseObserverCaptor.getValue();
assertEquals(1, lbRequestObservers.size());
lbRequestObserver = lbRequestObservers.poll();
verify(lbRequestObserver).onNext(eq(expectedInitialRequest));
inOrder.verify(helper).refreshNameResolution();
// Fail the retry after spending 4ns
fakeClock.forwardNanos(4);
lbResponseObserver.onError(Status.UNAVAILABLE.asException());
// Will be on the first retry (10ns) of backoff sequence 2.
inOrder.verify(backoffPolicy2).nextBackoffNanos();
assertEquals(1, fakeClock.numPendingTasks(LB_RPC_RETRY_TASK_FILTER));
inOrder.verify(helper).refreshNameResolution();
// Fast-forward to a moment before the retry, the time spent in the last try is deducted.
fakeClock.forwardNanos(10 - 4 - 1);
verifyNoMoreInteractions(mockLbService);
// Then time for retry
fakeClock.forwardNanos(1);
inOrder.verify(mockLbService).balanceLoad(lbResponseObserverCaptor.capture());
assertEquals(1, lbRequestObservers.size());
lbRequestObserver = lbRequestObservers.poll();
verify(lbRequestObserver).onNext(eq(expectedInitialRequest));
assertEquals(0, fakeClock.numPendingTasks(LB_RPC_RETRY_TASK_FILTER));
// Wrapping up
verify(backoffPolicyProvider, times(2)).get();
verify(backoffPolicy1, times(2)).nextBackoffNanos();
verify(backoffPolicy2, times(1)).nextBackoffNanos();
verify(helper, times(4)).refreshNameResolution();
}
use of io.grpc.lb.v1.LoadBalanceRequest in project grpc-java by grpc.
the class GrpclbLoadBalancerTest method grpclbWorking.
@Test
public void grpclbWorking() {
InOrder inOrder = inOrder(helper, subchannelPool);
List<EquivalentAddressGroup> grpclbBalancerList = createResolvedBalancerAddresses(1);
deliverResolvedAddresses(Collections.<EquivalentAddressGroup>emptyList(), grpclbBalancerList);
// Fallback timer is started as soon as the addresses are resolved.
assertEquals(1, fakeClock.numPendingTasks(FALLBACK_MODE_TASK_FILTER));
verify(helper).createOobChannel(eq(xattr(grpclbBalancerList)), eq(lbAuthority(0) + NO_USE_AUTHORITY_SUFFIX));
assertEquals(1, fakeOobChannels.size());
ManagedChannel oobChannel = fakeOobChannels.poll();
verify(mockLbService).balanceLoad(lbResponseObserverCaptor.capture());
StreamObserver<LoadBalanceResponse> lbResponseObserver = lbResponseObserverCaptor.getValue();
assertEquals(1, lbRequestObservers.size());
StreamObserver<LoadBalanceRequest> lbRequestObserver = lbRequestObservers.poll();
verify(lbRequestObserver).onNext(eq(LoadBalanceRequest.newBuilder().setInitialRequest(InitialLoadBalanceRequest.newBuilder().setName(SERVICE_AUTHORITY).build()).build()));
// Simulate receiving LB response
List<ServerEntry> backends1 = Arrays.asList(new ServerEntry("127.0.0.1", 2000, "token0001"), new ServerEntry("127.0.0.1", 2010, "token0002"));
inOrder.verify(helper, never()).updateBalancingState(any(ConnectivityState.class), any(SubchannelPicker.class));
logs.clear();
lbResponseObserver.onNext(buildInitialResponse());
assertThat(logs).containsExactly("INFO: [grpclb-<api.google.com>] Got an LB initial response: " + buildInitialResponse());
logs.clear();
lbResponseObserver.onNext(buildLbResponse(backends1));
inOrder.verify(subchannelPool).takeOrCreateSubchannel(eq(new EquivalentAddressGroup(backends1.get(0).addr, LB_BACKEND_ATTRS)), any(Attributes.class));
inOrder.verify(subchannelPool).takeOrCreateSubchannel(eq(new EquivalentAddressGroup(backends1.get(1).addr, LB_BACKEND_ATTRS)), any(Attributes.class));
assertEquals(2, mockSubchannels.size());
Subchannel subchannel1 = mockSubchannels.poll();
Subchannel subchannel2 = mockSubchannels.poll();
verify(subchannel1).requestConnection();
verify(subchannel2).requestConnection();
assertEquals(new EquivalentAddressGroup(backends1.get(0).addr, LB_BACKEND_ATTRS), subchannel1.getAddresses());
assertEquals(new EquivalentAddressGroup(backends1.get(1).addr, LB_BACKEND_ATTRS), subchannel2.getAddresses());
deliverSubchannelState(subchannel1, ConnectivityStateInfo.forNonError(CONNECTING));
deliverSubchannelState(subchannel2, ConnectivityStateInfo.forNonError(CONNECTING));
inOrder.verify(helper).updateBalancingState(eq(CONNECTING), pickerCaptor.capture());
RoundRobinPicker picker0 = (RoundRobinPicker) pickerCaptor.getValue();
assertThat(picker0.dropList).containsExactly(null, null);
assertThat(picker0.pickList).containsExactly(BUFFER_ENTRY);
inOrder.verifyNoMoreInteractions();
assertThat(logs).containsExactly("DEBUG: [grpclb-<api.google.com>] Got an LB response: " + buildLbResponse(backends1)).inOrder();
logs.clear();
// Let subchannels be connected
deliverSubchannelState(subchannel2, ConnectivityStateInfo.forNonError(READY));
inOrder.verify(helper).updateBalancingState(eq(READY), pickerCaptor.capture());
RoundRobinPicker picker1 = (RoundRobinPicker) pickerCaptor.getValue();
assertThat(picker1.dropList).containsExactly(null, null);
assertThat(picker1.pickList).containsExactly(new BackendEntry(subchannel2, getLoadRecorder(), "token0002"));
deliverSubchannelState(subchannel1, ConnectivityStateInfo.forNonError(READY));
inOrder.verify(helper).updateBalancingState(eq(READY), pickerCaptor.capture());
RoundRobinPicker picker2 = (RoundRobinPicker) pickerCaptor.getValue();
assertThat(picker2.dropList).containsExactly(null, null);
assertThat(picker2.pickList).containsExactly(new BackendEntry(subchannel1, getLoadRecorder(), "token0001"), new BackendEntry(subchannel2, getLoadRecorder(), "token0002")).inOrder();
// Disconnected subchannels
verify(subchannel1).requestConnection();
deliverSubchannelState(subchannel1, ConnectivityStateInfo.forNonError(IDLE));
verify(subchannel1, times(2)).requestConnection();
inOrder.verify(helper).refreshNameResolution();
inOrder.verify(helper).updateBalancingState(eq(READY), pickerCaptor.capture());
RoundRobinPicker picker3 = (RoundRobinPicker) pickerCaptor.getValue();
assertThat(picker3.dropList).containsExactly(null, null);
assertThat(picker3.pickList).containsExactly(new BackendEntry(subchannel2, getLoadRecorder(), "token0002"));
deliverSubchannelState(subchannel1, ConnectivityStateInfo.forNonError(CONNECTING));
inOrder.verifyNoMoreInteractions();
// As long as there is at least one READY subchannel, round robin will work.
ConnectivityStateInfo errorState1 = ConnectivityStateInfo.forTransientFailure(Status.UNAVAILABLE.withDescription("error1"));
deliverSubchannelState(subchannel1, errorState1);
inOrder.verify(helper).refreshNameResolution();
inOrder.verifyNoMoreInteractions();
// If no subchannel is READY, some with error and the others are IDLE, will report CONNECTING
verify(subchannel2).requestConnection();
deliverSubchannelState(subchannel2, ConnectivityStateInfo.forNonError(IDLE));
verify(subchannel2, times(2)).requestConnection();
inOrder.verify(helper).updateBalancingState(eq(CONNECTING), pickerCaptor.capture());
RoundRobinPicker picker4 = (RoundRobinPicker) pickerCaptor.getValue();
assertThat(picker4.dropList).containsExactly(null, null);
assertThat(picker4.pickList).containsExactly(BUFFER_ENTRY);
// Update backends, with a drop entry
List<ServerEntry> backends2 = Arrays.asList(// New address
new ServerEntry("127.0.0.1", 2030, "token0003"), // drop
new ServerEntry("token0003"), // Existing address with token changed
new ServerEntry("127.0.0.1", 2010, "token0004"), // New address appearing second time
new ServerEntry("127.0.0.1", 2030, "token0005"), // drop
new ServerEntry("token0006"));
verify(subchannelPool, never()).returnSubchannel(same(subchannel1), any(ConnectivityStateInfo.class));
lbResponseObserver.onNext(buildLbResponse(backends2));
assertThat(logs).containsExactly("DEBUG: [grpclb-<api.google.com>] Got an LB response: " + buildLbResponse(backends2)).inOrder();
logs.clear();
// not in backends2, closed
verify(subchannelPool).returnSubchannel(same(subchannel1), same(errorState1));
// backends2[2], will be kept
verify(subchannelPool, never()).returnSubchannel(same(subchannel2), any(ConnectivityStateInfo.class));
inOrder.verify(subchannelPool, never()).takeOrCreateSubchannel(eq(new EquivalentAddressGroup(backends2.get(2).addr, LB_BACKEND_ATTRS)), any(Attributes.class));
inOrder.verify(subchannelPool).takeOrCreateSubchannel(eq(new EquivalentAddressGroup(backends2.get(0).addr, LB_BACKEND_ATTRS)), any(Attributes.class));
ConnectivityStateInfo errorOnCachedSubchannel1 = ConnectivityStateInfo.forTransientFailure(Status.UNAVAILABLE.withDescription("You can get this error even if you are cached"));
deliverSubchannelState(subchannel1, errorOnCachedSubchannel1);
assertEquals(1, mockSubchannels.size());
Subchannel subchannel3 = mockSubchannels.poll();
verify(subchannel3).requestConnection();
assertEquals(new EquivalentAddressGroup(backends2.get(0).addr, LB_BACKEND_ATTRS), subchannel3.getAddresses());
inOrder.verify(helper).updateBalancingState(eq(CONNECTING), pickerCaptor.capture());
RoundRobinPicker picker7 = (RoundRobinPicker) pickerCaptor.getValue();
assertThat(picker7.dropList).containsExactly(null, new DropEntry(getLoadRecorder(), "token0003"), null, null, new DropEntry(getLoadRecorder(), "token0006")).inOrder();
assertThat(picker7.pickList).containsExactly(BUFFER_ENTRY);
// State updates on obsolete subchannel1 will only be passed to the pool
deliverSubchannelState(subchannel1, ConnectivityStateInfo.forNonError(READY));
deliverSubchannelState(subchannel1, ConnectivityStateInfo.forTransientFailure(Status.UNAVAILABLE));
deliverSubchannelState(subchannel1, ConnectivityStateInfo.forNonError(SHUTDOWN));
deliverSubchannelState(subchannel3, ConnectivityStateInfo.forNonError(READY));
inOrder.verify(helper).updateBalancingState(eq(READY), pickerCaptor.capture());
RoundRobinPicker picker8 = (RoundRobinPicker) pickerCaptor.getValue();
assertThat(picker8.dropList).containsExactly(null, new DropEntry(getLoadRecorder(), "token0003"), null, null, new DropEntry(getLoadRecorder(), "token0006")).inOrder();
// subchannel2 is still IDLE, thus not in the active list
assertThat(picker8.pickList).containsExactly(new BackendEntry(subchannel3, getLoadRecorder(), "token0003"), new BackendEntry(subchannel3, getLoadRecorder(), "token0005")).inOrder();
// subchannel2 becomes READY and makes it into the list
deliverSubchannelState(subchannel2, ConnectivityStateInfo.forNonError(READY));
inOrder.verify(helper).updateBalancingState(eq(READY), pickerCaptor.capture());
RoundRobinPicker picker9 = (RoundRobinPicker) pickerCaptor.getValue();
assertThat(picker9.dropList).containsExactly(null, new DropEntry(getLoadRecorder(), "token0003"), null, null, new DropEntry(getLoadRecorder(), "token0006")).inOrder();
assertThat(picker9.pickList).containsExactly(new BackendEntry(subchannel3, getLoadRecorder(), "token0003"), new BackendEntry(subchannel2, getLoadRecorder(), "token0004"), new BackendEntry(subchannel3, getLoadRecorder(), "token0005")).inOrder();
verify(subchannelPool, never()).returnSubchannel(same(subchannel3), any(ConnectivityStateInfo.class));
// Update backends, with no entry
lbResponseObserver.onNext(buildLbResponse(Collections.<ServerEntry>emptyList()));
verify(subchannelPool).returnSubchannel(same(subchannel2), eq(ConnectivityStateInfo.forNonError(READY)));
verify(subchannelPool).returnSubchannel(same(subchannel3), eq(ConnectivityStateInfo.forNonError(READY)));
inOrder.verify(helper).updateBalancingState(eq(TRANSIENT_FAILURE), pickerCaptor.capture());
RoundRobinPicker picker10 = (RoundRobinPicker) pickerCaptor.getValue();
assertThat(picker10.dropList).isEmpty();
assertThat(picker10.pickList).containsExactly(new ErrorEntry(GrpclbState.NO_AVAILABLE_BACKENDS_STATUS));
assertFalse(oobChannel.isShutdown());
assertEquals(0, lbRequestObservers.size());
verify(lbRequestObserver, never()).onCompleted();
verify(lbRequestObserver, never()).onError(any(Throwable.class));
// Load reporting was not requested, thus never scheduled
assertEquals(0, fakeClock.numPendingTasks(LOAD_REPORTING_TASK_FILTER));
verify(subchannelPool, never()).clear();
balancer.shutdown();
verify(subchannelPool).clear();
}
use of io.grpc.lb.v1.LoadBalanceRequest in project grpc-java by grpc.
the class GrpclbLoadBalancerTest method grpclbFallback_breakLbStreamBeforeFallbackTimerExpires.
@Test
public void grpclbFallback_breakLbStreamBeforeFallbackTimerExpires() {
long loadReportIntervalMillis = 1983;
InOrder inOrder = inOrder(helper, subchannelPool);
// Create balancer and backend addresses
List<EquivalentAddressGroup> backendList = createResolvedBackendAddresses(2);
List<EquivalentAddressGroup> grpclbBalancerList = createResolvedBalancerAddresses(1);
deliverResolvedAddresses(backendList, grpclbBalancerList);
inOrder.verify(helper).createOobChannel(eq(xattr(grpclbBalancerList)), eq(lbAuthority(0) + NO_USE_AUTHORITY_SUFFIX));
// Attempted to connect to balancer
assertThat(fakeOobChannels).hasSize(1);
verify(mockLbService).balanceLoad(lbResponseObserverCaptor.capture());
StreamObserver<LoadBalanceResponse> lbResponseObserver = lbResponseObserverCaptor.getValue();
assertThat(lbRequestObservers).hasSize(1);
StreamObserver<LoadBalanceRequest> lbRequestObserver = lbRequestObservers.poll();
verify(lbRequestObserver).onNext(eq(LoadBalanceRequest.newBuilder().setInitialRequest(InitialLoadBalanceRequest.newBuilder().setName(SERVICE_AUTHORITY).build()).build()));
lbResponseObserver.onNext(buildInitialResponse(loadReportIntervalMillis));
// We don't care if these methods have been run.
inOrder.verify(helper, atLeast(0)).getSynchronizationContext();
inOrder.verify(helper, atLeast(0)).getScheduledExecutorService();
inOrder.verifyNoMoreInteractions();
assertEquals(1, fakeClock.numPendingTasks(FALLBACK_MODE_TASK_FILTER));
// ///////////////////////////////////////////
// Break the LB stream before timer expires
// ///////////////////////////////////////////
Status streamError = Status.UNAVAILABLE.withDescription("OOB stream broken");
lbResponseObserver.onError(streamError.asException());
// Fallback time has been short-circuited
assertEquals(0, fakeClock.numPendingTasks(FALLBACK_MODE_TASK_FILTER));
// Fall back to the backends from resolver
fallbackTestVerifyUseOfFallbackBackendLists(inOrder, Arrays.asList(backendList.get(0), backendList.get(1)));
// A new stream is created
verify(mockLbService, times(2)).balanceLoad(lbResponseObserverCaptor.capture());
assertThat(lbRequestObservers).hasSize(1);
lbRequestObserver = lbRequestObservers.poll();
verify(lbRequestObserver).onNext(eq(LoadBalanceRequest.newBuilder().setInitialRequest(InitialLoadBalanceRequest.newBuilder().setName(SERVICE_AUTHORITY).build()).build()));
// ////////////////////////////////////////////////////////////////////
// Name resolver sends new resolution results without any backend addr
// ////////////////////////////////////////////////////////////////////
deliverResolvedAddresses(Collections.<EquivalentAddressGroup>emptyList(), grpclbBalancerList);
// Still in fallback logic, except that the backend list is empty
for (Subchannel subchannel : mockSubchannels) {
verify(subchannelPool).returnSubchannel(eq(subchannel), any(ConnectivityStateInfo.class));
}
// RPC error status includes error of balancer stream
inOrder.verify(helper).updateBalancingState(eq(TRANSIENT_FAILURE), pickerCaptor.capture());
PickResult result = pickerCaptor.getValue().pickSubchannel(mock(PickSubchannelArgs.class));
assertThat(result.getStatus().getCode()).isEqualTo(Code.UNAVAILABLE);
assertThat(result.getStatus().getDescription()).startsWith(GrpclbState.NO_FALLBACK_BACKENDS_STATUS.getDescription());
assertThat(result.getStatus().getDescription()).contains(streamError.getDescription());
}
use of io.grpc.lb.v1.LoadBalanceRequest in project grpc-java by grpc.
the class GrpclbLoadBalancerTest method subtestGrpclbFallbackInitialTimeout.
// Fallback or not within the period of the initial timeout.
private void subtestGrpclbFallbackInitialTimeout(boolean timerExpires) {
long loadReportIntervalMillis = 1983;
InOrder inOrder = inOrder(helper, subchannelPool);
// Create balancer and backend addresses
List<EquivalentAddressGroup> backendList = createResolvedBackendAddresses(2);
List<EquivalentAddressGroup> grpclbBalancerList = createResolvedBalancerAddresses(1);
deliverResolvedAddresses(backendList, grpclbBalancerList);
inOrder.verify(helper).createOobChannel(eq(xattr(grpclbBalancerList)), eq(lbAuthority(0) + NO_USE_AUTHORITY_SUFFIX));
// Attempted to connect to balancer
assertEquals(1, fakeOobChannels.size());
ManagedChannel oobChannel = fakeOobChannels.poll();
verify(mockLbService).balanceLoad(lbResponseObserverCaptor.capture());
StreamObserver<LoadBalanceResponse> lbResponseObserver = lbResponseObserverCaptor.getValue();
assertEquals(1, lbRequestObservers.size());
StreamObserver<LoadBalanceRequest> lbRequestObserver = lbRequestObservers.poll();
verify(lbRequestObserver).onNext(eq(LoadBalanceRequest.newBuilder().setInitialRequest(InitialLoadBalanceRequest.newBuilder().setName(SERVICE_AUTHORITY).build()).build()));
lbResponseObserver.onNext(buildInitialResponse(loadReportIntervalMillis));
// We don't care if these methods have been run.
inOrder.verify(helper, atLeast(0)).getSynchronizationContext();
inOrder.verify(helper, atLeast(0)).getScheduledExecutorService();
inOrder.verifyNoMoreInteractions();
assertEquals(1, fakeClock.numPendingTasks(FALLBACK_MODE_TASK_FILTER));
fakeClock.forwardTime(GrpclbState.FALLBACK_TIMEOUT_MS - 1, TimeUnit.MILLISECONDS);
assertEquals(1, fakeClock.numPendingTasks(FALLBACK_MODE_TASK_FILTER));
// ////////////////////////////////
if (timerExpires) {
logs.clear();
fakeClock.forwardTime(1, TimeUnit.MILLISECONDS);
assertEquals(0, fakeClock.numPendingTasks(FALLBACK_MODE_TASK_FILTER));
assertThat(logs).containsExactly("INFO: [grpclb-<api.google.com>] Using fallback backends").inOrder();
// Fall back to the backends from resolver
fallbackTestVerifyUseOfFallbackBackendLists(inOrder, backendList);
assertFalse(oobChannel.isShutdown());
verify(lbRequestObserver, never()).onCompleted();
}
// ////////////////////////////////////////////////////////////////////
// Name resolver sends new resolution results without any backend addr
// ////////////////////////////////////////////////////////////////////
grpclbBalancerList = createResolvedBalancerAddresses(2);
deliverResolvedAddresses(Collections.<EquivalentAddressGroup>emptyList(), grpclbBalancerList);
// New addresses are updated to the OobChannel
inOrder.verify(helper).updateOobChannelAddresses(same(oobChannel), eq(xattr(grpclbBalancerList)));
if (timerExpires) {
// Still in fallback logic, except that the backend list is empty
for (Subchannel subchannel : mockSubchannels) {
verify(subchannelPool).returnSubchannel(eq(subchannel), any(ConnectivityStateInfo.class));
}
// RPC error status includes message of balancer RPC timeout
inOrder.verify(helper).updateBalancingState(eq(TRANSIENT_FAILURE), pickerCaptor.capture());
PickResult result = pickerCaptor.getValue().pickSubchannel(mock(PickSubchannelArgs.class));
assertThat(result.getStatus().getCode()).isEqualTo(Code.UNAVAILABLE);
assertThat(result.getStatus().getDescription()).startsWith(GrpclbState.NO_FALLBACK_BACKENDS_STATUS.getDescription());
assertThat(result.getStatus().getDescription()).contains(GrpclbState.BALANCER_TIMEOUT_STATUS.getDescription());
}
// //////////////////////////////////////////////////////////////
// Name resolver sends new resolution results with backend addrs
// //////////////////////////////////////////////////////////////
// prevents the cached subchannel to be used
subchannelPool.clear();
backendList = createResolvedBackendAddresses(2);
grpclbBalancerList = createResolvedBalancerAddresses(1);
deliverResolvedAddresses(backendList, grpclbBalancerList);
// New LB address is updated to the OobChannel
inOrder.verify(helper).updateOobChannelAddresses(same(oobChannel), eq(xattr(grpclbBalancerList)));
if (timerExpires) {
// New backend addresses are used for fallback
fallbackTestVerifyUseOfFallbackBackendLists(inOrder, Arrays.asList(backendList.get(0), backendList.get(1)));
}
// //////////////////////////////////////////////
if (timerExpires) {
Status streamError = Status.UNAVAILABLE.withDescription("OOB stream broken");
lbResponseObserver.onError(streamError.asException());
// The error will NOT propagate to picker because fallback list is in use.
inOrder.verify(helper, never()).updateBalancingState(any(ConnectivityState.class), any(SubchannelPicker.class));
// A new stream is created
verify(mockLbService, times(2)).balanceLoad(lbResponseObserverCaptor.capture());
lbResponseObserver = lbResponseObserverCaptor.getValue();
assertEquals(1, lbRequestObservers.size());
lbRequestObserver = lbRequestObservers.poll();
verify(lbRequestObserver).onNext(eq(LoadBalanceRequest.newBuilder().setInitialRequest(InitialLoadBalanceRequest.newBuilder().setName(SERVICE_AUTHORITY).build()).build()));
}
// ///////////////////////////////
// Balancer returns a server list
// ///////////////////////////////
List<ServerEntry> serverList = Arrays.asList(new ServerEntry("127.0.0.1", 2000, "token0001"), new ServerEntry("127.0.0.1", 2010, "token0002"));
lbResponseObserver.onNext(buildInitialResponse());
lbResponseObserver.onNext(buildLbResponse(serverList));
// Balancer-provided server list now in effect
fallbackTestVerifyUseOfBalancerBackendLists(inOrder, serverList);
// /////////////////////////////////////////////////////////////
// New backend addresses from resolver outside of fallback mode
// /////////////////////////////////////////////////////////////
backendList = createResolvedBackendAddresses(1);
grpclbBalancerList = createResolvedBalancerAddresses(1);
deliverResolvedAddresses(backendList, grpclbBalancerList);
// Will not affect the round robin list at all
inOrder.verify(helper, never()).updateBalancingState(any(ConnectivityState.class), any(SubchannelPicker.class));
// No fallback timeout timer scheduled.
assertEquals(0, fakeClock.numPendingTasks(FALLBACK_MODE_TASK_FILTER));
}
Aggregations