mirror of https://github.com/grpc/grpc-java.git
core: Propagate authority override from LB exactly once
Setting the authority is only useful when creating a real stream, as there will be a following pick otherwise. In addition, DelayedStream will buffer each call to setAuthority() in a list and we don't want that memory usage. Note that no LBs are using this feature yet, so users would not have been exposed to the memory use. We also needed to setAuthority() when the LB selected a subchannel on the first pick attempt.
This commit is contained in:
parent
7153ff8522
commit
90aefb26e7
|
@ -140,9 +140,15 @@ final class DelayedClientTransport implements ManagedClientTransport {
|
||||||
ClientTransport transport = GrpcUtil.getTransportFromPickResult(pickResult,
|
ClientTransport transport = GrpcUtil.getTransportFromPickResult(pickResult,
|
||||||
callOptions.isWaitForReady());
|
callOptions.isWaitForReady());
|
||||||
if (transport != null) {
|
if (transport != null) {
|
||||||
return transport.newStream(
|
ClientStream stream = transport.newStream(
|
||||||
args.getMethodDescriptor(), args.getHeaders(), callOptions,
|
args.getMethodDescriptor(), args.getHeaders(), callOptions,
|
||||||
tracers);
|
tracers);
|
||||||
|
// User code provided authority takes precedence over the LB provided one; this will be
|
||||||
|
// overwritten by ClientCallImpl if the application sets an authority override
|
||||||
|
if (pickResult.getAuthorityOverride() != null) {
|
||||||
|
stream.setAuthority(pickResult.getAuthorityOverride());
|
||||||
|
}
|
||||||
|
return stream;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// This picker's conclusion is "buffer". If there hasn't been a newer picker set (possible
|
// This picker's conclusion is "buffer". If there hasn't been a newer picker set (possible
|
||||||
|
@ -287,10 +293,6 @@ final class DelayedClientTransport implements ManagedClientTransport {
|
||||||
for (final PendingStream stream : toProcess) {
|
for (final PendingStream stream : toProcess) {
|
||||||
PickResult pickResult = picker.pickSubchannel(stream.args);
|
PickResult pickResult = picker.pickSubchannel(stream.args);
|
||||||
CallOptions callOptions = stream.args.getCallOptions();
|
CallOptions callOptions = stream.args.getCallOptions();
|
||||||
// User code provided authority takes precedence over the LB provided one.
|
|
||||||
if (callOptions.getAuthority() == null && pickResult.getAuthorityOverride() != null) {
|
|
||||||
stream.setAuthority(pickResult.getAuthorityOverride());
|
|
||||||
}
|
|
||||||
final ClientTransport transport = GrpcUtil.getTransportFromPickResult(pickResult,
|
final ClientTransport transport = GrpcUtil.getTransportFromPickResult(pickResult,
|
||||||
callOptions.isWaitForReady());
|
callOptions.isWaitForReady());
|
||||||
if (transport != null) {
|
if (transport != null) {
|
||||||
|
@ -301,7 +303,7 @@ final class DelayedClientTransport implements ManagedClientTransport {
|
||||||
if (callOptions.getExecutor() != null) {
|
if (callOptions.getExecutor() != null) {
|
||||||
executor = callOptions.getExecutor();
|
executor = callOptions.getExecutor();
|
||||||
}
|
}
|
||||||
Runnable runnable = stream.createRealStream(transport);
|
Runnable runnable = stream.createRealStream(transport, pickResult.getAuthorityOverride());
|
||||||
if (runnable != null) {
|
if (runnable != null) {
|
||||||
executor.execute(runnable);
|
executor.execute(runnable);
|
||||||
}
|
}
|
||||||
|
@ -354,7 +356,7 @@ final class DelayedClientTransport implements ManagedClientTransport {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Runnable may be null. */
|
/** Runnable may be null. */
|
||||||
private Runnable createRealStream(ClientTransport transport) {
|
private Runnable createRealStream(ClientTransport transport, String authorityOverride) {
|
||||||
ClientStream realStream;
|
ClientStream realStream;
|
||||||
Context origContext = context.attach();
|
Context origContext = context.attach();
|
||||||
try {
|
try {
|
||||||
|
@ -364,6 +366,13 @@ final class DelayedClientTransport implements ManagedClientTransport {
|
||||||
} finally {
|
} finally {
|
||||||
context.detach(origContext);
|
context.detach(origContext);
|
||||||
}
|
}
|
||||||
|
if (authorityOverride != null) {
|
||||||
|
// User code provided authority takes precedence over the LB provided one; this will be
|
||||||
|
// overwritten by an enqueud call from ClientCallImpl if the application sets an authority
|
||||||
|
// override. We must call the real stream directly because stream.start() has likely already
|
||||||
|
// been called on the delayed stream.
|
||||||
|
realStream.setAuthority(authorityOverride);
|
||||||
|
}
|
||||||
return setStream(realStream);
|
return setStream(realStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -503,26 +503,11 @@ public class DelayedClientTransportTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void reprocess_authorityOverridePresentInCallOptions_authorityOverrideFromLbIsIgnored() {
|
public void reprocess_authorityOverrideFromLb() {
|
||||||
DelayedStream delayedStream = (DelayedStream) delayedTransport.newStream(
|
InOrder inOrder = inOrder(mockRealStream);
|
||||||
method, headers, callOptions, tracers);
|
|
||||||
delayedStream.start(mock(ClientStreamListener.class));
|
|
||||||
SubchannelPicker picker = mock(SubchannelPicker.class);
|
|
||||||
PickResult pickResult = PickResult.withSubchannel(
|
|
||||||
mockSubchannel, null, "authority-override-hostname-from-lb");
|
|
||||||
when(picker.pickSubchannel(any(PickSubchannelArgs.class))).thenReturn(pickResult);
|
|
||||||
|
|
||||||
delayedTransport.reprocess(picker);
|
|
||||||
fakeExecutor.runDueTasks();
|
|
||||||
|
|
||||||
verify(mockRealStream, never()).setAuthority("authority-override-hostname-from-lb");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void
|
|
||||||
reprocess_authorityOverrideNotInCallOptions_authorityOverrideFromLbIsSetIntoStream() {
|
|
||||||
DelayedStream delayedStream = (DelayedStream) delayedTransport.newStream(
|
DelayedStream delayedStream = (DelayedStream) delayedTransport.newStream(
|
||||||
method, headers, callOptions.withAuthority(null), tracers);
|
method, headers, callOptions.withAuthority(null), tracers);
|
||||||
|
delayedStream.setAuthority("authority-override-from-calloptions");
|
||||||
delayedStream.start(mock(ClientStreamListener.class));
|
delayedStream.start(mock(ClientStreamListener.class));
|
||||||
SubchannelPicker picker = mock(SubchannelPicker.class);
|
SubchannelPicker picker = mock(SubchannelPicker.class);
|
||||||
PickResult pickResult = PickResult.withSubchannel(
|
PickResult pickResult = PickResult.withSubchannel(
|
||||||
|
@ -536,7 +521,10 @@ public class DelayedClientTransportTest {
|
||||||
delayedTransport.reprocess(picker);
|
delayedTransport.reprocess(picker);
|
||||||
fakeExecutor.runDueTasks();
|
fakeExecutor.runDueTasks();
|
||||||
|
|
||||||
verify(mockRealStream).setAuthority("authority-override-hostname-from-lb");
|
// Must be set before start(), and may be overwritten
|
||||||
|
inOrder.verify(mockRealStream).setAuthority("authority-override-hostname-from-lb");
|
||||||
|
inOrder.verify(mockRealStream).setAuthority("authority-override-from-calloptions");
|
||||||
|
inOrder.verify(mockRealStream).start(any(ClientStreamListener.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -563,28 +551,26 @@ public class DelayedClientTransportTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void newStream_assignsTransport_authorityFromCallOptionsSupersedesAuthorityFromLB() {
|
public void newStream_authorityOverrideFromLb() {
|
||||||
|
InOrder inOrder = inOrder(mockRealStream);
|
||||||
SubchannelPicker picker = mock(SubchannelPicker.class);
|
SubchannelPicker picker = mock(SubchannelPicker.class);
|
||||||
AbstractSubchannel subchannel = mock(AbstractSubchannel.class);
|
|
||||||
when(subchannel.getInternalSubchannel()).thenReturn(mockInternalSubchannel);
|
|
||||||
PickResult pickResult = PickResult.withSubchannel(
|
PickResult pickResult = PickResult.withSubchannel(
|
||||||
subchannel, null, "authority-override-hostname-from-lb");
|
mockSubchannel, null, "authority-override-hostname-from-lb");
|
||||||
when(picker.pickSubchannel(any(PickSubchannelArgs.class))).thenReturn(pickResult);
|
when(picker.pickSubchannel(any(PickSubchannelArgs.class))).thenReturn(pickResult);
|
||||||
ArgumentCaptor<CallOptions> callOptionsArgumentCaptor =
|
|
||||||
ArgumentCaptor.forClass(CallOptions.class);
|
|
||||||
when(mockRealTransport.newStream(
|
when(mockRealTransport.newStream(
|
||||||
any(MethodDescriptor.class), any(Metadata.class), callOptionsArgumentCaptor.capture(),
|
any(MethodDescriptor.class), any(Metadata.class), any(CallOptions.class), any()))
|
||||||
ArgumentMatchers.<ClientStreamTracer[]>any()))
|
|
||||||
.thenReturn(mockRealStream);
|
.thenReturn(mockRealStream);
|
||||||
delayedTransport.reprocess(picker);
|
delayedTransport.reprocess(picker);
|
||||||
verifyNoMoreInteractions(picker);
|
|
||||||
verifyNoMoreInteractions(transportListener);
|
|
||||||
|
|
||||||
CallOptions callOptions =
|
ClientStream stream = delayedTransport.newStream(method, headers, callOptions, tracers);
|
||||||
CallOptions.DEFAULT.withAuthority("authority-override-hosstname-from-calloptions");
|
assertThat(stream).isSameInstanceAs(mockRealStream);
|
||||||
delayedTransport.newStream(method, headers, callOptions, tracers);
|
stream.setAuthority("authority-override-from-calloptions");
|
||||||
assertThat(callOptionsArgumentCaptor.getValue().getAuthority()).isEqualTo(
|
stream.start(mock(ClientStreamListener.class));
|
||||||
"authority-override-hosstname-from-calloptions");
|
|
||||||
|
// Must be set before start(), and may be overwritten
|
||||||
|
inOrder.verify(mockRealStream).setAuthority("authority-override-hostname-from-lb");
|
||||||
|
inOrder.verify(mockRealStream).setAuthority("authority-override-from-calloptions");
|
||||||
|
inOrder.verify(mockRealStream).start(any(ClientStreamListener.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
Loading…
Reference in New Issue