use of io.grpc.LoadBalancer.SubchannelPicker in project grpc-java by grpc.
the class OobChannel method setSubchannel.
// Must be called only once, right after the OobChannel is created.
void setSubchannel(final InternalSubchannel subchannel) {
log.log(Level.FINE, "[{0}] Created with [{1}]", new Object[] { this, subchannel });
subchannelImpl = new SubchannelImpl() {
@Override
public void shutdown() {
subchannel.shutdown();
}
@Override
ClientTransport obtainActiveTransport() {
return subchannel.obtainActiveTransport();
}
@Override
public void requestConnection() {
subchannel.obtainActiveTransport();
}
@Override
public EquivalentAddressGroup getAddresses() {
return subchannel.getAddressGroup();
}
@Override
public Attributes getAttributes() {
return Attributes.EMPTY;
}
};
subchannelPicker = new SubchannelPicker() {
final PickResult result = PickResult.withSubchannel(subchannelImpl);
@Override
public PickResult pickSubchannel(PickSubchannelArgs args) {
return result;
}
};
delayedTransport.reprocess(subchannelPicker);
}
use of io.grpc.LoadBalancer.SubchannelPicker in project grpc-java by grpc.
the class DelayedClientTransportTest method reprocessSemantics.
@Test
public void reprocessSemantics() {
CallOptions failFastCallOptions = CallOptions.DEFAULT.withOption(SHARD_ID, 1);
CallOptions waitForReadyCallOptions = CallOptions.DEFAULT.withOption(SHARD_ID, 2).withWaitForReady();
AbstractSubchannel subchannel1 = mock(AbstractSubchannel.class);
AbstractSubchannel subchannel2 = mock(AbstractSubchannel.class);
AbstractSubchannel subchannel3 = mock(AbstractSubchannel.class);
when(mockRealTransport.newStream(any(MethodDescriptor.class), any(Metadata.class), any(CallOptions.class), ArgumentMatchers.<ClientStreamTracer[]>any())).thenReturn(mockRealStream);
when(mockRealTransport2.newStream(any(MethodDescriptor.class), any(Metadata.class), any(CallOptions.class), ArgumentMatchers.<ClientStreamTracer[]>any())).thenReturn(mockRealStream2);
when(subchannel1.getInternalSubchannel()).thenReturn(newTransportProvider(mockRealTransport));
when(subchannel2.getInternalSubchannel()).thenReturn(newTransportProvider(mockRealTransport2));
when(subchannel3.getInternalSubchannel()).thenReturn(newTransportProvider(null));
// Fail-fast streams
DelayedStream ff1 = (DelayedStream) delayedTransport.newStream(method, headers, failFastCallOptions, tracers);
ff1.start(mock(ClientStreamListener.class));
ff1.halfClose();
PickSubchannelArgsImpl ff1args = new PickSubchannelArgsImpl(method, headers, failFastCallOptions);
verify(transportListener).transportInUse(true);
DelayedStream ff2 = (DelayedStream) delayedTransport.newStream(method2, headers2, failFastCallOptions, tracers);
PickSubchannelArgsImpl ff2args = new PickSubchannelArgsImpl(method2, headers2, failFastCallOptions);
DelayedStream ff3 = (DelayedStream) delayedTransport.newStream(method, headers, failFastCallOptions, tracers);
PickSubchannelArgsImpl ff3args = new PickSubchannelArgsImpl(method, headers, failFastCallOptions);
DelayedStream ff4 = (DelayedStream) delayedTransport.newStream(method2, headers2, failFastCallOptions, tracers);
PickSubchannelArgsImpl ff4args = new PickSubchannelArgsImpl(method2, headers2, failFastCallOptions);
// Wait-for-ready streams
FakeClock wfr3Executor = new FakeClock();
DelayedStream wfr1 = (DelayedStream) delayedTransport.newStream(method, headers, waitForReadyCallOptions, tracers);
PickSubchannelArgsImpl wfr1args = new PickSubchannelArgsImpl(method, headers, waitForReadyCallOptions);
DelayedStream wfr2 = (DelayedStream) delayedTransport.newStream(method2, headers2, waitForReadyCallOptions, tracers);
PickSubchannelArgsImpl wfr2args = new PickSubchannelArgsImpl(method2, headers2, waitForReadyCallOptions);
CallOptions wfr3callOptions = waitForReadyCallOptions.withExecutor(wfr3Executor.getScheduledExecutorService());
DelayedStream wfr3 = (DelayedStream) delayedTransport.newStream(method, headers, wfr3callOptions, tracers);
wfr3.start(mock(ClientStreamListener.class));
wfr3.halfClose();
PickSubchannelArgsImpl wfr3args = new PickSubchannelArgsImpl(method, headers, wfr3callOptions);
DelayedStream wfr4 = (DelayedStream) delayedTransport.newStream(method2, headers2, waitForReadyCallOptions, tracers);
PickSubchannelArgsImpl wfr4args = new PickSubchannelArgsImpl(method2, headers2, waitForReadyCallOptions);
assertEquals(8, delayedTransport.getPendingStreamsCount());
// First reprocess(). Some will proceed, some will fail and the rest will stay buffered.
SubchannelPicker picker = mock(SubchannelPicker.class);
when(picker.pickSubchannel(any(PickSubchannelArgs.class))).thenReturn(// ff1: proceed
PickResult.withSubchannel(subchannel1), // ff2: fail
PickResult.withError(Status.UNAVAILABLE), // ff3: stay
PickResult.withSubchannel(subchannel3), // ff4: stay
PickResult.withNoResult(), // wfr1: proceed
PickResult.withSubchannel(subchannel2), // wfr2: stay
PickResult.withError(Status.RESOURCE_EXHAUSTED), // wfr3: stay
PickResult.withSubchannel(subchannel3));
InOrder inOrder = inOrder(picker);
delayedTransport.reprocess(picker);
assertEquals(5, delayedTransport.getPendingStreamsCount());
inOrder.verify(picker).pickSubchannel(ff1args);
inOrder.verify(picker).pickSubchannel(ff2args);
inOrder.verify(picker).pickSubchannel(ff3args);
inOrder.verify(picker).pickSubchannel(ff4args);
inOrder.verify(picker).pickSubchannel(wfr1args);
inOrder.verify(picker).pickSubchannel(wfr2args);
inOrder.verify(picker).pickSubchannel(wfr3args);
inOrder.verify(picker).pickSubchannel(wfr4args);
inOrder.verifyNoMoreInteractions();
// Make sure that streams are created and started immediately, not in any executor. This is
// necessary during shut down to guarantee that when DelayedClientTransport terminates, all
// streams are now owned by a real transport (which should prevent the Channel from
// terminating).
// ff1 and wfr1 went through
verify(mockRealTransport).newStream(method, headers, failFastCallOptions, tracers);
verify(mockRealTransport2).newStream(method, headers, waitForReadyCallOptions, tracers);
assertSame(mockRealStream, ff1.getRealStream());
assertSame(mockRealStream2, wfr1.getRealStream());
verify(mockRealStream).start(any(ClientStreamListener.class));
// But also verify that non-start()-related calls are run within the Executor, since they may be
// slow.
verify(mockRealStream, never()).halfClose();
fakeExecutor.runDueTasks();
assertEquals(0, fakeExecutor.numPendingTasks());
verify(mockRealStream).halfClose();
// The ff2 has failed due to picker returning an error
assertSame(Status.UNAVAILABLE, ((FailingClientStream) ff2.getRealStream()).getError());
// Other streams are still buffered
assertNull(ff3.getRealStream());
assertNull(ff4.getRealStream());
assertNull(wfr2.getRealStream());
assertNull(wfr3.getRealStream());
assertNull(wfr4.getRealStream());
// Second reprocess(). All existing streams will proceed.
picker = mock(SubchannelPicker.class);
when(picker.pickSubchannel(any(PickSubchannelArgs.class))).thenReturn(// ff3
PickResult.withSubchannel(subchannel1), // ff4
PickResult.withSubchannel(subchannel2), // wfr2
PickResult.withSubchannel(subchannel2), // wfr3
PickResult.withSubchannel(subchannel1), // wfr4
PickResult.withSubchannel(subchannel2), // wfr5 (not yet created)
PickResult.withNoResult());
inOrder = inOrder(picker);
assertEquals(0, wfr3Executor.numPendingTasks());
verify(transportListener, never()).transportInUse(false);
delayedTransport.reprocess(picker);
assertEquals(0, delayedTransport.getPendingStreamsCount());
verify(transportListener).transportInUse(false);
// ff3
inOrder.verify(picker).pickSubchannel(ff3args);
// ff4
inOrder.verify(picker).pickSubchannel(ff4args);
// wfr2
inOrder.verify(picker).pickSubchannel(wfr2args);
// wfr3
inOrder.verify(picker).pickSubchannel(wfr3args);
// wfr4
inOrder.verify(picker).pickSubchannel(wfr4args);
inOrder.verifyNoMoreInteractions();
fakeExecutor.runDueTasks();
assertEquals(0, fakeExecutor.numPendingTasks());
assertSame(mockRealStream, ff3.getRealStream());
assertSame(mockRealStream2, ff4.getRealStream());
assertSame(mockRealStream2, wfr2.getRealStream());
assertSame(mockRealStream2, wfr4.getRealStream());
assertSame(mockRealStream, wfr3.getRealStream());
// If there is an executor in the CallOptions, it will be used to create the real stream.
// 1 for ff1
verify(mockRealStream, times(1)).halfClose();
wfr3Executor.runDueTasks();
verify(mockRealStream, times(2)).halfClose();
// New streams will use the last picker
DelayedStream wfr5 = (DelayedStream) delayedTransport.newStream(method, headers, waitForReadyCallOptions, tracers);
assertNull(wfr5.getRealStream());
inOrder.verify(picker).pickSubchannel(new PickSubchannelArgsImpl(method, headers, waitForReadyCallOptions));
inOrder.verifyNoMoreInteractions();
assertEquals(1, delayedTransport.getPendingStreamsCount());
// wfr5 will stop delayed transport from terminating
delayedTransport.shutdown(SHUTDOWN_STATUS);
verify(transportListener).transportShutdown(same(SHUTDOWN_STATUS));
verify(transportListener, never()).transportTerminated();
// ... until it's gone
picker = mock(SubchannelPicker.class);
when(picker.pickSubchannel(any(PickSubchannelArgs.class))).thenReturn(PickResult.withSubchannel(subchannel1));
delayedTransport.reprocess(picker);
verify(picker).pickSubchannel(new PickSubchannelArgsImpl(method, headers, waitForReadyCallOptions));
fakeExecutor.runDueTasks();
assertSame(mockRealStream, wfr5.getRealStream());
assertEquals(0, delayedTransport.getPendingStreamsCount());
verify(transportListener).transportTerminated();
}
use of io.grpc.LoadBalancer.SubchannelPicker in project grpc-java by grpc.
the class DelayedClientTransport method newStream.
/**
* If a {@link SubchannelPicker} is being, or has been provided via {@link #reprocess}, the last
* picker will be consulted.
*
* <p>Otherwise, if the delayed transport is not shutdown, then a {@link PendingStream} is
* returned; if the transport is shutdown, then a {@link FailingClientStream} is returned.
*/
@Override
public final ClientStream newStream(MethodDescriptor<?, ?> method, Metadata headers, CallOptions callOptions, ClientStreamTracer[] tracers) {
try {
PickSubchannelArgs args = new PickSubchannelArgsImpl(method, headers, callOptions);
SubchannelPicker picker = null;
long pickerVersion = -1;
while (true) {
synchronized (lock) {
if (shutdownStatus != null) {
return new FailingClientStream(shutdownStatus, tracers);
}
if (lastPicker == null) {
return createPendingStream(args, tracers);
}
// Check for second time through the loop, and whether anything changed
if (picker != null && pickerVersion == lastPickerVersion) {
return createPendingStream(args, tracers);
}
picker = lastPicker;
pickerVersion = lastPickerVersion;
}
PickResult pickResult = picker.pickSubchannel(args);
ClientTransport transport = GrpcUtil.getTransportFromPickResult(pickResult, callOptions.isWaitForReady());
if (transport != null) {
return transport.newStream(args.getMethodDescriptor(), args.getHeaders(), args.getCallOptions(), tracers);
}
// This picker's conclusion is "buffer". If there hasn't been a newer picker set (possible
// race with reprocess()), we will buffer it. Otherwise, will try with the new picker.
}
} finally {
syncContext.drain();
}
}
use of io.grpc.LoadBalancer.SubchannelPicker in project grpc-java by grpc.
the class RoundRobinLoadBalancerTest method subchannelStateIsolation.
@Test
public void subchannelStateIsolation() throws Exception {
Iterator<Subchannel> subchannelIterator = subchannels.values().iterator();
Subchannel sc1 = subchannelIterator.next();
Subchannel sc2 = subchannelIterator.next();
Subchannel sc3 = subchannelIterator.next();
loadBalancer.handleResolvedAddresses(ResolvedAddresses.newBuilder().setAddresses(servers).setAttributes(Attributes.EMPTY).build());
verify(sc1, times(1)).requestConnection();
verify(sc2, times(1)).requestConnection();
verify(sc3, times(1)).requestConnection();
deliverSubchannelState(sc1, ConnectivityStateInfo.forNonError(READY));
deliverSubchannelState(sc2, ConnectivityStateInfo.forNonError(READY));
deliverSubchannelState(sc3, ConnectivityStateInfo.forNonError(READY));
deliverSubchannelState(sc2, ConnectivityStateInfo.forNonError(IDLE));
deliverSubchannelState(sc3, ConnectivityStateInfo.forTransientFailure(Status.UNAVAILABLE));
verify(mockHelper, times(6)).updateBalancingState(stateCaptor.capture(), pickerCaptor.capture());
Iterator<ConnectivityState> stateIterator = stateCaptor.getAllValues().iterator();
Iterator<SubchannelPicker> pickers = pickerCaptor.getAllValues().iterator();
// The picker is incrementally updated as subchannels become READY
assertEquals(CONNECTING, stateIterator.next());
assertThat(pickers.next()).isInstanceOf(EmptyPicker.class);
assertEquals(READY, stateIterator.next());
assertThat(getList(pickers.next())).containsExactly(sc1);
assertEquals(READY, stateIterator.next());
assertThat(getList(pickers.next())).containsExactly(sc1, sc2);
assertEquals(READY, stateIterator.next());
assertThat(getList(pickers.next())).containsExactly(sc1, sc2, sc3);
// The IDLE subchannel is dropped from the picker, but a reconnection is requested
assertEquals(READY, stateIterator.next());
assertThat(getList(pickers.next())).containsExactly(sc1, sc3);
verify(sc2, times(2)).requestConnection();
// The failing subchannel is dropped from the picker, with no requested reconnect
assertEquals(READY, stateIterator.next());
assertThat(getList(pickers.next())).containsExactly(sc1);
verify(sc3, times(1)).requestConnection();
assertThat(stateIterator.hasNext()).isFalse();
assertThat(pickers.hasNext()).isFalse();
}
use of io.grpc.LoadBalancer.SubchannelPicker in project grpc-java by grpc.
the class RoundRobinLoadBalancerTest method pickerEmptyList.
@Test
public void pickerEmptyList() throws Exception {
SubchannelPicker picker = new EmptyPicker(Status.UNKNOWN);
assertEquals(null, picker.pickSubchannel(mockArgs).getSubchannel());
assertEquals(Status.UNKNOWN, picker.pickSubchannel(mockArgs).getStatus());
}
Aggregations