Skip to content

Commit 574ee56

Browse files
committed
[FnApi] Add support header metadata in ApiServiceDescriptor. Sdks indicate they support this via StandardProtocols.API_SERVICE_DESCRIPTOR_NAME. Runners may use such metadata to distinguish between incoming fnapi streams. A concrete usage for this is to indicate for different bundles to utilize different data streams to provide isolation.
1 parent 945e6e1 commit 574ee56

6 files changed

Lines changed: 82 additions & 1 deletion

File tree

model/pipeline/src/main/proto/org/apache/beam/model/pipeline/v1/beam_runner_api.proto

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1689,6 +1689,12 @@ message StandardProtocols {
16891689
// Indicates whether the SDK supports multimap state.
16901690
MULTIMAP_STATE = 12
16911691
[(beam_urn) = "beam:protocol:multimap_state:v1"];
1692+
1693+
// Indicates that the SDK supports the field ApiServiceDescriptor.header_metadata being
1694+
// non-empty. The header metadata specified will be added to when connecting to
1695+
// the service.
1696+
API_SERVICE_DESCRIPTOR_HEADER_METADATA = 13
1697+
[(beam_urn) = "beam:protocol:api_service_descriptor_header_metadata:v1"];
16921698
}
16931699
}
16941700

model/pipeline/src/main/proto/org/apache/beam/model/pipeline/v1/endpoints.proto

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ message ApiServiceDescriptor {
3737
// url is already being performed in a trusted context (e.g. localhost,
3838
// private network).
3939
AuthenticationSpec authentication = 2;
40+
41+
// (Optional) Additional ASCII header metadata that will be sent with calls to
42+
// the endpoint. SDKs indicate that they handle
43+
// this field with the `StandardProtocols.API_SERVICE_DESCRIPTOR_HEADER_METADATA`.
44+
map<string, string> header_metadata = 3;
4045
}
4146

4247
message AuthenticationSpec {

sdks/java/core/src/main/java/org/apache/beam/sdk/fn/channel/ManagedChannelFactory.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@
2424
import org.apache.beam.vendor.grpc.v1p69p0.io.grpc.ClientInterceptor;
2525
import org.apache.beam.vendor.grpc.v1p69p0.io.grpc.ManagedChannel;
2626
import org.apache.beam.vendor.grpc.v1p69p0.io.grpc.ManagedChannelBuilder;
27+
import org.apache.beam.vendor.grpc.v1p69p0.io.grpc.Metadata;
2728
import org.apache.beam.vendor.grpc.v1p69p0.io.grpc.inprocess.InProcessChannelBuilder;
2829
import org.apache.beam.vendor.grpc.v1p69p0.io.grpc.netty.NettyChannelBuilder;
30+
import org.apache.beam.vendor.grpc.v1p69p0.io.grpc.stub.MetadataUtils;
2931
import org.apache.beam.vendor.grpc.v1p69p0.io.netty.channel.epoll.EpollDomainSocketChannel;
3032
import org.apache.beam.vendor.grpc.v1p69p0.io.netty.channel.epoll.EpollEventLoopGroup;
3133
import org.apache.beam.vendor.grpc.v1p69p0.io.netty.channel.epoll.EpollSocketChannel;
@@ -95,6 +97,14 @@ public ManagedChannel forDescriptor(ApiServiceDescriptor apiServiceDescriptor) {
9597
// https://github.com/grpc/proposal/blob/master/A6-client-retries.md#when-retries-are-valid
9698
.disableRetry()
9799
.intercept(interceptors);
100+
if (!apiServiceDescriptor.getHeaderMetadataMap().isEmpty()) {
101+
Metadata metadata = new Metadata();
102+
apiServiceDescriptor
103+
.getHeaderMetadataMap()
104+
.forEach((k, v) -> metadata.put(Metadata.Key.of(k, Metadata.ASCII_STRING_MARSHALLER), v));
105+
channelBuilder =
106+
channelBuilder.intercept(MetadataUtils.newAttachHeadersInterceptor(metadata));
107+
}
98108
if (directExecutor) {
99109
channelBuilder = channelBuilder.directExecutor();
100110
}

sdks/java/core/src/main/java/org/apache/beam/sdk/util/construction/Environments.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,8 @@ public static Set<String> getJavaCapabilities() {
522522
capabilities.add(BeamUrns.getUrn(StandardProtocols.Enum.SDK_CONSUMING_RECEIVED_DATA));
523523
capabilities.add(BeamUrns.getUrn(StandardProtocols.Enum.ORDERED_LIST_STATE));
524524
capabilities.add(BeamUrns.getUrn(StandardProtocols.Enum.MULTIMAP_STATE));
525+
capabilities.add(
526+
BeamUrns.getUrn(StandardProtocols.Enum.API_SERVICE_DESCRIPTOR_HEADER_METADATA));
525527
return capabilities.build();
526528
}
527529

sdks/java/core/src/test/java/org/apache/beam/sdk/fn/channel/ManagedChannelFactoryTest.java

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@
2222
import static org.junit.Assume.assumeTrue;
2323

2424
import org.apache.beam.model.pipeline.v1.Endpoints;
25+
import org.apache.beam.vendor.grpc.v1p69p0.io.grpc.CallOptions;
26+
import org.apache.beam.vendor.grpc.v1p69p0.io.grpc.ClientCall;
2527
import org.apache.beam.vendor.grpc.v1p69p0.io.grpc.ManagedChannel;
28+
import org.apache.beam.vendor.grpc.v1p69p0.io.grpc.Metadata;
29+
import org.apache.beam.vendor.grpc.v1p69p0.io.grpc.testing.TestMethodDescriptors;
2630
import org.apache.commons.lang3.SystemUtils;
2731
import org.junit.Rule;
2832
import org.junit.Test;
@@ -42,6 +46,60 @@ public void testDefaultChannel() {
4246
ManagedChannel channel =
4347
ManagedChannelFactory.createDefault().forDescriptor(apiServiceDescriptor);
4448
assertEquals("localhost:123", channel.authority());
49+
ClientCall<Void, Void> call =
50+
channel.newCall(TestMethodDescriptors.voidMethod(), CallOptions.DEFAULT);
51+
Metadata headers = new Metadata();
52+
call.start(new ClientCall.Listener<>() {}, headers);
53+
for (String key : headers.keys()) {
54+
assertTrue(
55+
"Unexpected key "
56+
+ key
57+
+ " with value "
58+
+ headers.get(Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER)),
59+
key.startsWith("grpc-"));
60+
}
61+
call.cancel("Done", null);
62+
channel.shutdownNow();
63+
}
64+
65+
@Test
66+
public void testDefaultChannelWithStreamName() {
67+
Endpoints.ApiServiceDescriptor apiServiceDescriptor =
68+
Endpoints.ApiServiceDescriptor.newBuilder()
69+
.setUrl("localhost:123")
70+
.putHeaderMetadata("key", "value")
71+
.putHeaderMetadata("other-key", "other-value")
72+
.build();
73+
ManagedChannel channel =
74+
ManagedChannelFactory.createDefault().forDescriptor(apiServiceDescriptor);
75+
assertEquals("localhost:123", channel.authority());
76+
77+
ClientCall<Void, Void> call =
78+
channel.newCall(TestMethodDescriptors.voidMethod(), CallOptions.DEFAULT);
79+
Metadata headers = new Metadata();
80+
call.start(new ClientCall.Listener<>() {}, headers);
81+
boolean foundKey = false;
82+
boolean foundOtherKey = false;
83+
for (String key : headers.keys()) {
84+
if (key.equals("key")) {
85+
foundKey = true;
86+
assertEquals("value", headers.get(Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER)));
87+
} else if (key.equals("other-key")) {
88+
foundOtherKey = true;
89+
assertEquals(
90+
"other-value", headers.get(Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER)));
91+
} else {
92+
assertTrue(
93+
"Unexpected key "
94+
+ key
95+
+ " with value "
96+
+ headers.get(Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER)),
97+
key.startsWith("grpc-"));
98+
}
99+
}
100+
assertTrue(foundKey);
101+
assertTrue(foundOtherKey);
102+
call.cancel("Done", null);
45103
channel.shutdownNow();
46104
}
47105

sdks/java/harness/src/main/java/org/apache/beam/fn/harness/data/BeamFnDataGrpcClient.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,6 @@ private BeamFnDataGrpcMultiplexer getClientFor(
110110
new BeamFnDataGrpcMultiplexer(
111111
descriptor,
112112
outboundObserverFactory,
113-
BeamFnDataGrpc.newStub(channelFactory.apply(apiServiceDescriptor))::data));
113+
BeamFnDataGrpc.newStub(channelFactory.apply(descriptor))::data));
114114
}
115115
}

0 commit comments

Comments
 (0)