Skip to content

Commit 90aefb2

Browse files
committed
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.
1 parent 7153ff8 commit 90aefb2

File tree

2 files changed

+36
-41
lines changed

2 files changed

+36
-41
lines changed

core/src/main/java/io/grpc/internal/DelayedClientTransport.java

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,15 @@ public final ClientStream newStream(
140140
ClientTransport transport = GrpcUtil.getTransportFromPickResult(pickResult,
141141
callOptions.isWaitForReady());
142142
if (transport != null) {
143-
return transport.newStream(
143+
ClientStream stream = transport.newStream(
144144
args.getMethodDescriptor(), args.getHeaders(), callOptions,
145145
tracers);
146+
// User code provided authority takes precedence over the LB provided one; this will be
147+
// overwritten by ClientCallImpl if the application sets an authority override
148+
if (pickResult.getAuthorityOverride() != null) {
149+
stream.setAuthority(pickResult.getAuthorityOverride());
150+
}
151+
return stream;
146152
}
147153
}
148154
// This picker's conclusion is "buffer". If there hasn't been a newer picker set (possible
@@ -287,10 +293,6 @@ final void reprocess(@Nullable SubchannelPicker picker) {
287293
for (final PendingStream stream : toProcess) {
288294
PickResult pickResult = picker.pickSubchannel(stream.args);
289295
CallOptions callOptions = stream.args.getCallOptions();
290-
// User code provided authority takes precedence over the LB provided one.
291-
if (callOptions.getAuthority() == null && pickResult.getAuthorityOverride() != null) {
292-
stream.setAuthority(pickResult.getAuthorityOverride());
293-
}
294296
final ClientTransport transport = GrpcUtil.getTransportFromPickResult(pickResult,
295297
callOptions.isWaitForReady());
296298
if (transport != null) {
@@ -301,7 +303,7 @@ final void reprocess(@Nullable SubchannelPicker picker) {
301303
if (callOptions.getExecutor() != null) {
302304
executor = callOptions.getExecutor();
303305
}
304-
Runnable runnable = stream.createRealStream(transport);
306+
Runnable runnable = stream.createRealStream(transport, pickResult.getAuthorityOverride());
305307
if (runnable != null) {
306308
executor.execute(runnable);
307309
}
@@ -354,7 +356,7 @@ private PendingStream(PickSubchannelArgs args, ClientStreamTracer[] tracers) {
354356
}
355357

356358
/** Runnable may be null. */
357-
private Runnable createRealStream(ClientTransport transport) {
359+
private Runnable createRealStream(ClientTransport transport, String authorityOverride) {
358360
ClientStream realStream;
359361
Context origContext = context.attach();
360362
try {
@@ -364,6 +366,13 @@ private Runnable createRealStream(ClientTransport transport) {
364366
} finally {
365367
context.detach(origContext);
366368
}
369+
if (authorityOverride != null) {
370+
// User code provided authority takes precedence over the LB provided one; this will be
371+
// overwritten by an enqueud call from ClientCallImpl if the application sets an authority
372+
// override. We must call the real stream directly because stream.start() has likely already
373+
// been called on the delayed stream.
374+
realStream.setAuthority(authorityOverride);
375+
}
367376
return setStream(realStream);
368377
}
369378

core/src/test/java/io/grpc/internal/DelayedClientTransportTest.java

Lines changed: 20 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -503,26 +503,11 @@ public void uncaughtException(Thread t, Throwable e) {
503503
}
504504

505505
@Test
506-
public void reprocess_authorityOverridePresentInCallOptions_authorityOverrideFromLbIsIgnored() {
507-
DelayedStream delayedStream = (DelayedStream) delayedTransport.newStream(
508-
method, headers, callOptions, tracers);
509-
delayedStream.start(mock(ClientStreamListener.class));
510-
SubchannelPicker picker = mock(SubchannelPicker.class);
511-
PickResult pickResult = PickResult.withSubchannel(
512-
mockSubchannel, null, "authority-override-hostname-from-lb");
513-
when(picker.pickSubchannel(any(PickSubchannelArgs.class))).thenReturn(pickResult);
514-
515-
delayedTransport.reprocess(picker);
516-
fakeExecutor.runDueTasks();
517-
518-
verify(mockRealStream, never()).setAuthority("authority-override-hostname-from-lb");
519-
}
520-
521-
@Test
522-
public void
523-
reprocess_authorityOverrideNotInCallOptions_authorityOverrideFromLbIsSetIntoStream() {
506+
public void reprocess_authorityOverrideFromLb() {
507+
InOrder inOrder = inOrder(mockRealStream);
524508
DelayedStream delayedStream = (DelayedStream) delayedTransport.newStream(
525509
method, headers, callOptions.withAuthority(null), tracers);
510+
delayedStream.setAuthority("authority-override-from-calloptions");
526511
delayedStream.start(mock(ClientStreamListener.class));
527512
SubchannelPicker picker = mock(SubchannelPicker.class);
528513
PickResult pickResult = PickResult.withSubchannel(
@@ -536,7 +521,10 @@ public void reprocess_authorityOverridePresentInCallOptions_authorityOverrideFro
536521
delayedTransport.reprocess(picker);
537522
fakeExecutor.runDueTasks();
538523

539-
verify(mockRealStream).setAuthority("authority-override-hostname-from-lb");
524+
// Must be set before start(), and may be overwritten
525+
inOrder.verify(mockRealStream).setAuthority("authority-override-hostname-from-lb");
526+
inOrder.verify(mockRealStream).setAuthority("authority-override-from-calloptions");
527+
inOrder.verify(mockRealStream).start(any(ClientStreamListener.class));
540528
}
541529

542530
@Test
@@ -563,28 +551,26 @@ public void reprocess_NoPendingStream() {
563551
}
564552

565553
@Test
566-
public void newStream_assignsTransport_authorityFromCallOptionsSupersedesAuthorityFromLB() {
554+
public void newStream_authorityOverrideFromLb() {
555+
InOrder inOrder = inOrder(mockRealStream);
567556
SubchannelPicker picker = mock(SubchannelPicker.class);
568-
AbstractSubchannel subchannel = mock(AbstractSubchannel.class);
569-
when(subchannel.getInternalSubchannel()).thenReturn(mockInternalSubchannel);
570557
PickResult pickResult = PickResult.withSubchannel(
571-
subchannel, null, "authority-override-hostname-from-lb");
558+
mockSubchannel, null, "authority-override-hostname-from-lb");
572559
when(picker.pickSubchannel(any(PickSubchannelArgs.class))).thenReturn(pickResult);
573-
ArgumentCaptor<CallOptions> callOptionsArgumentCaptor =
574-
ArgumentCaptor.forClass(CallOptions.class);
575560
when(mockRealTransport.newStream(
576-
any(MethodDescriptor.class), any(Metadata.class), callOptionsArgumentCaptor.capture(),
577-
ArgumentMatchers.<ClientStreamTracer[]>any()))
561+
any(MethodDescriptor.class), any(Metadata.class), any(CallOptions.class), any()))
578562
.thenReturn(mockRealStream);
579563
delayedTransport.reprocess(picker);
580-
verifyNoMoreInteractions(picker);
581-
verifyNoMoreInteractions(transportListener);
582564

583-
CallOptions callOptions =
584-
CallOptions.DEFAULT.withAuthority("authority-override-hosstname-from-calloptions");
585-
delayedTransport.newStream(method, headers, callOptions, tracers);
586-
assertThat(callOptionsArgumentCaptor.getValue().getAuthority()).isEqualTo(
587-
"authority-override-hosstname-from-calloptions");
565+
ClientStream stream = delayedTransport.newStream(method, headers, callOptions, tracers);
566+
assertThat(stream).isSameInstanceAs(mockRealStream);
567+
stream.setAuthority("authority-override-from-calloptions");
568+
stream.start(mock(ClientStreamListener.class));
569+
570+
// Must be set before start(), and may be overwritten
571+
inOrder.verify(mockRealStream).setAuthority("authority-override-hostname-from-lb");
572+
inOrder.verify(mockRealStream).setAuthority("authority-override-from-calloptions");
573+
inOrder.verify(mockRealStream).start(any(ClientStreamListener.class));
588574
}
589575

590576
@Test

0 commit comments

Comments
 (0)