This is an automated email from the ASF dual-hosted git repository.

lcwik pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/beam.git


The following commit(s) were added to refs/heads/master by this push:
     new 8dc9bc1  [BEAM-4127] Flink streaming translation for portable 
pipeline. (#5358)
8dc9bc1 is described below

commit 8dc9bc1d16e70249f30689a6f941cb1707b6b564
Author: Thomas Weise <[email protected]>
AuthorDate: Tue May 15 17:03:59 2018 -0700

    [BEAM-4127] Flink streaming translation for portable pipeline. (#5358)
---
 .../FlinkBatchPortablePipelineTranslator.java      |   3 +
 .../FlinkStreamingPortablePipelineTranslator.java  | 443 +++++++++++++++++++++
 .../flink/FlinkStreamingTransformTranslators.java  |   2 +-
 3 files changed, 447 insertions(+), 1 deletion(-)

diff --git 
a/runners/flink/src/main/java/org/apache/beam/runners/flink/FlinkBatchPortablePipelineTranslator.java
 
b/runners/flink/src/main/java/org/apache/beam/runners/flink/FlinkBatchPortablePipelineTranslator.java
index 0c8ed5d..72fe10e 100644
--- 
a/runners/flink/src/main/java/org/apache/beam/runners/flink/FlinkBatchPortablePipelineTranslator.java
+++ 
b/runners/flink/src/main/java/org/apache/beam/runners/flink/FlinkBatchPortablePipelineTranslator.java
@@ -273,6 +273,9 @@ public class FlinkBatchPortablePipelineTranslator
     } catch (InvalidProtocolBufferException e) {
       throw new IllegalArgumentException(e);
     }
+    //TODO: https://issues.apache.org/jira/browse/BEAM-4296
+    // This only works for well known window fns, we should defer this 
execution to the SDK
+    // if the WindowFn can't be parsed or just defer it all the time.
     WindowFn<T, ? extends BoundedWindow> windowFn =
         (WindowFn<T, ? extends BoundedWindow>)
             
WindowingStrategyTranslation.windowFnFromProto(payload.getWindowFn());
diff --git 
a/runners/flink/src/main/java/org/apache/beam/runners/flink/FlinkStreamingPortablePipelineTranslator.java
 
b/runners/flink/src/main/java/org/apache/beam/runners/flink/FlinkStreamingPortablePipelineTranslator.java
new file mode 100644
index 0000000..b7bc8ca
--- /dev/null
+++ 
b/runners/flink/src/main/java/org/apache/beam/runners/flink/FlinkStreamingPortablePipelineTranslator.java
@@ -0,0 +1,443 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.beam.runners.flink;
+
+import com.google.common.collect.HashMultiset;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import com.google.protobuf.InvalidProtocolBufferException;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.beam.model.pipeline.v1.RunnerApi;
+import org.apache.beam.runners.core.SystemReduceFn;
+import org.apache.beam.runners.core.construction.PTransformTranslation;
+import org.apache.beam.runners.core.construction.PipelineOptionsTranslation;
+import org.apache.beam.runners.core.construction.RehydratedComponents;
+import org.apache.beam.runners.core.construction.WindowingStrategyTranslation;
+import org.apache.beam.runners.core.construction.graph.ExecutableStage;
+import org.apache.beam.runners.core.construction.graph.PipelineNode;
+import org.apache.beam.runners.core.construction.graph.QueryablePipeline;
+import org.apache.beam.runners.flink.translation.functions.FlinkAssignWindows;
+import org.apache.beam.runners.flink.translation.types.CoderTypeInformation;
+import 
org.apache.beam.runners.flink.translation.wrappers.streaming.DoFnOperator;
+import 
org.apache.beam.runners.flink.translation.wrappers.streaming.SingletonKeyedWorkItem;
+import 
org.apache.beam.runners.flink.translation.wrappers.streaming.SingletonKeyedWorkItemCoder;
+import 
org.apache.beam.runners.flink.translation.wrappers.streaming.WindowDoFnOperator;
+import 
org.apache.beam.runners.flink.translation.wrappers.streaming.WorkItemKeySelector;
+import org.apache.beam.runners.fnexecution.provisioning.JobInfo;
+import org.apache.beam.runners.fnexecution.wire.WireCoders;
+import org.apache.beam.sdk.coders.ByteArrayCoder;
+import org.apache.beam.sdk.coders.Coder;
+import org.apache.beam.sdk.coders.IterableCoder;
+import org.apache.beam.sdk.coders.KvCoder;
+import org.apache.beam.sdk.coders.VoidCoder;
+import org.apache.beam.sdk.options.PipelineOptions;
+import org.apache.beam.sdk.transforms.windowing.BoundedWindow;
+import org.apache.beam.sdk.transforms.windowing.GlobalWindow;
+import org.apache.beam.sdk.transforms.windowing.WindowFn;
+import org.apache.beam.sdk.util.WindowedValue;
+import org.apache.beam.sdk.util.WindowedValue.WindowedValueCoder;
+import org.apache.beam.sdk.values.KV;
+import org.apache.beam.sdk.values.TupleTag;
+import org.apache.beam.sdk.values.WindowingStrategy;
+import org.apache.flink.api.common.functions.FlatMapFunction;
+import org.apache.flink.api.common.typeinfo.TypeInformation;
+import org.apache.flink.streaming.api.datastream.DataStream;
+import org.apache.flink.streaming.api.datastream.DataStreamSource;
+import org.apache.flink.streaming.api.datastream.KeyedStream;
+import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
+import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
+import org.apache.flink.streaming.api.operators.OneInputStreamOperator;
+import org.apache.flink.util.Collector;
+
+/**
+ * Translate an unbounded portable pipeline representation into a Flink 
pipeline representation.
+ */
+public class FlinkStreamingPortablePipelineTranslator implements 
FlinkPortablePipelineTranslator<
+        FlinkStreamingPortablePipelineTranslator.StreamingTranslationContext> {
+
+  /**
+   * Creates a streaming translation context. The resulting Flink execution 
dag will live in a new
+   * {@link StreamExecutionEnvironment}.
+   */
+  public static StreamingTranslationContext createTranslationContext(JobInfo 
jobInfo) {
+    PipelineOptions pipelineOptions;
+    try {
+      pipelineOptions = 
PipelineOptionsTranslation.fromProto(jobInfo.pipelineOptions());
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+    StreamExecutionEnvironment executionEnvironment =
+            FlinkExecutionEnvironments.createStreamExecutionEnvironment(
+                    pipelineOptions.as(FlinkPipelineOptions.class));
+    return new StreamingTranslationContext(jobInfo, pipelineOptions, 
executionEnvironment);
+  }
+
+  /**
+   * Streaming translation context. Stores metadata about known 
PCollections/DataStreams and holds
+   * the Flink {@link StreamExecutionEnvironment} that the execution plan will 
be applied to.
+   */
+  public static class StreamingTranslationContext
+          implements FlinkPortablePipelineTranslator.TranslationContext {
+
+    private final JobInfo jobInfo;
+    private final PipelineOptions options;
+    private final StreamExecutionEnvironment executionEnvironment;
+    private final Map<String, DataStream<?>> dataStreams;
+
+    private StreamingTranslationContext(
+            JobInfo jobInfo,
+            PipelineOptions options,
+            StreamExecutionEnvironment executionEnvironment) {
+      this.jobInfo = jobInfo;
+      this.options = options;
+      this.executionEnvironment = executionEnvironment;
+      dataStreams = new HashMap<>();
+
+    }
+
+    @Override
+    public JobInfo getJobInfo() {
+      return jobInfo;
+    }
+
+    public PipelineOptions getPipelineOptions() {
+      return options;
+    }
+
+    public StreamExecutionEnvironment getExecutionEnvironment() {
+      return executionEnvironment;
+    }
+
+    public <T> void addDataStream(String pCollectionId, DataStream<T> dataSet) 
{
+      dataStreams.put(pCollectionId, dataSet);
+    }
+
+    public <T> DataStream<T> getDataStreamOrThrow(String pCollectionId) {
+      DataStream<T> dataSet = (DataStream<T>) dataStreams.get(pCollectionId);
+      if (dataSet == null) {
+        throw new IllegalArgumentException(
+                String.format("Unknown datastream for id %s.", pCollectionId));
+      }
+      return dataSet;
+    }
+
+  }
+
+  interface PTransformTranslator<T> {
+    void translate(String id, RunnerApi.Pipeline pipeline, T t);
+  }
+
+  private final Map<String, PTransformTranslator<StreamingTranslationContext>>
+          urnToTransformTranslator;
+
+  FlinkStreamingPortablePipelineTranslator() {
+    ImmutableMap.Builder<String, 
PTransformTranslator<StreamingTranslationContext>> translatorMap =
+            ImmutableMap.builder();
+    translatorMap.put(PTransformTranslation.FLATTEN_TRANSFORM_URN,
+            this::translateFlatten);
+    translatorMap.put(PTransformTranslation.GROUP_BY_KEY_TRANSFORM_URN,
+            this::translateGroupByKey);
+    translatorMap.put(PTransformTranslation.IMPULSE_TRANSFORM_URN,
+            this::translateImpulse);
+    translatorMap.put(PTransformTranslation.ASSIGN_WINDOWS_TRANSFORM_URN,
+            this::translateAssignWindows);
+    translatorMap.put(ExecutableStage.URN,
+        this::translateExecutableStage);
+    translatorMap.put(PTransformTranslation.RESHUFFLE_URN,
+        this::translateReshuffle);
+    this.urnToTransformTranslator = translatorMap.build();
+  }
+
+
+  @Override
+  public void translate(StreamingTranslationContext context, 
RunnerApi.Pipeline pipeline) {
+    QueryablePipeline p = QueryablePipeline.forTransforms(
+        pipeline.getRootTransformIdsList(), pipeline.getComponents());
+    for (PipelineNode.PTransformNode transform : 
p.getTopologicallyOrderedTransforms()) {
+      urnToTransformTranslator.getOrDefault(
+              transform.getTransform().getSpec().getUrn(), this::urnNotFound)
+              .translate(transform.getId(), pipeline, context);
+    }
+
+  }
+
+  private void urnNotFound(
+          String id, RunnerApi.Pipeline pipeline,
+          FlinkStreamingPortablePipelineTranslator.TranslationContext context) 
{
+    throw new IllegalArgumentException(
+            String.format("Unknown type of URN %s for PTrasnform with id %s.",
+                    
pipeline.getComponents().getTransformsOrThrow(id).getSpec().getUrn(),
+                    id));
+  }
+
+  private <K, V> void translateReshuffle(
+      String id,
+      RunnerApi.Pipeline pipeline,
+      StreamingTranslationContext context) {
+    RunnerApi.PTransform transform = 
pipeline.getComponents().getTransformsOrThrow(id);
+    DataStream<WindowedValue<KV<K, V>>> inputDataStream =
+        context.getDataStreamOrThrow(
+            Iterables.getOnlyElement(transform.getInputsMap().values()));
+    
context.addDataStream(Iterables.getOnlyElement(transform.getOutputsMap().values()),
+        inputDataStream.rebalance());
+  }
+
+  private <T>  void translateFlatten(
+          String id,
+          RunnerApi.Pipeline pipeline,
+          StreamingTranslationContext context) {
+    Map<String, String> allInputs =
+            pipeline.getComponents().getTransformsOrThrow(id).getInputsMap();
+
+    if (allInputs.isEmpty()) {
+
+      // create an empty dummy source to satisfy downstream operations
+      // we cannot create an empty source in Flink, therefore we have to
+      // add the flatMap that simply never forwards the single element
+      DataStreamSource<String> dummySource =
+              context.getExecutionEnvironment().fromElements("dummy");
+
+      DataStream<WindowedValue<T>> result =
+              dummySource
+                      .<WindowedValue<T>>flatMap(
+                              (s, collector) -> {
+                                // never return anything
+                              })
+                      .returns(
+                              new CoderTypeInformation<>(
+                                      WindowedValue.getFullCoder(
+                                              (Coder<T>) VoidCoder.of(),
+                                              GlobalWindow.Coder.INSTANCE)));
+      context.addDataStream(Iterables.getOnlyElement(
+              
pipeline.getComponents().getTransformsOrThrow(id).getOutputsMap().values()), 
result);
+    } else {
+      DataStream<T> result = null;
+
+      // Determine DataStreams that we use as input several times. For those, 
we need to uniquify
+      // input streams because Flink seems to swallow watermarks when we have 
a union of one and
+      // the same stream.
+      HashMultiset<DataStream<T>> inputCounts = HashMultiset.create();
+      for (String input : allInputs.values()) {
+        DataStream<T> current = context.getDataStreamOrThrow(input);
+        inputCounts.add(current, 1);
+      }
+
+      for (String input : allInputs.values()) {
+        DataStream<T> current = context.getDataStreamOrThrow(input);
+        final int timesRequired = inputCounts.count(current);
+        if (timesRequired > 1) {
+          current = current.flatMap(new FlatMapFunction<T, T>() {
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            public void flatMap(T t, Collector<T> collector) {
+              collector.collect(t);
+            }
+          });
+        }
+        result = (result == null) ? current : result.union(current);
+      }
+
+      context.addDataStream(Iterables.getOnlyElement(
+              
pipeline.getComponents().getTransformsOrThrow(id).getOutputsMap().values()), 
result);
+    }
+  }
+
+  private <K, V> void translateGroupByKey(
+          String id,
+          RunnerApi.Pipeline pipeline,
+          StreamingTranslationContext context) {
+
+    RunnerApi.PTransform pTransform =
+            pipeline.getComponents().getTransformsOrThrow(id);
+    String inputPCollectionId =
+            Iterables.getOnlyElement(pTransform.getInputsMap().values());
+
+    RunnerApi.WindowingStrategy windowingStrategyProto =
+            pipeline.getComponents().getWindowingStrategiesOrThrow(
+                    pipeline.getComponents().getPcollectionsOrThrow(
+                            inputPCollectionId).getWindowingStrategyId());
+
+    DataStream<WindowedValue<KV<K, V>>> inputDataStream =
+            context.getDataStreamOrThrow(inputPCollectionId);
+
+    RehydratedComponents rehydratedComponents =
+            RehydratedComponents.forComponents(pipeline.getComponents());
+
+    WindowingStrategy<?, ?> windowingStrategy;
+    try {
+      windowingStrategy = WindowingStrategyTranslation.fromProto(
+                      windowingStrategyProto,
+                      rehydratedComponents);
+    } catch (InvalidProtocolBufferException e) {
+      throw new IllegalStateException(
+              String.format("Unable to hydrate GroupByKey windowing strategy 
%s.",
+                      windowingStrategyProto), e);
+    }
+
+    WindowedValueCoder<KV<K, V>> inputCoder = (WindowedValueCoder) 
instantiateCoder(
+            inputPCollectionId, pipeline.getComponents());
+    KvCoder<K, V> inputElementCoder = (KvCoder<K, V>) 
inputCoder.getValueCoder();
+
+    SingletonKeyedWorkItemCoder<K, V> workItemCoder = 
SingletonKeyedWorkItemCoder.of(
+            inputElementCoder.getKeyCoder(),
+            inputElementCoder.getValueCoder(),
+            windowingStrategy.getWindowFn().windowCoder());
+
+    WindowedValue.
+            FullWindowedValueCoder<SingletonKeyedWorkItem<K, V>> 
windowedWorkItemCoder =
+            WindowedValue.getFullCoder(
+                    workItemCoder,
+                    windowingStrategy.getWindowFn().windowCoder());
+
+    CoderTypeInformation<WindowedValue<SingletonKeyedWorkItem<K, V>>> 
workItemTypeInfo =
+            new CoderTypeInformation<>(windowedWorkItemCoder);
+
+    DataStream<WindowedValue<SingletonKeyedWorkItem<K, V>>> workItemStream =
+            inputDataStream
+                    .flatMap(new 
FlinkStreamingTransformTranslators.ToKeyedWorkItem<>())
+                    .returns(workItemTypeInfo)
+                    .name("ToKeyedWorkItem");
+
+    KeyedStream<WindowedValue<SingletonKeyedWorkItem<K, V>>, ByteBuffer>
+            keyedWorkItemStream =
+            workItemStream.keyBy(new 
WorkItemKeySelector<>(inputElementCoder.getKeyCoder()));
+
+    SystemReduceFn<K, V, Iterable<V>, Iterable<V>, BoundedWindow> reduceFn =
+            SystemReduceFn.buffering(inputElementCoder.getValueCoder());
+
+    Coder<Iterable<V>> accumulatorCoder = 
IterableCoder.of(inputElementCoder.getValueCoder());
+
+    Coder<WindowedValue<KV<K, Iterable<V>>>> outputCoder = 
WindowedValue.getFullCoder(
+            KvCoder.of(inputElementCoder.getKeyCoder(), accumulatorCoder),
+                    windowingStrategy.getWindowFn().windowCoder());
+
+    TypeInformation<WindowedValue<KV<K, Iterable<V>>>> outputTypeInfo =
+            new CoderTypeInformation<>(outputCoder);
+
+    TupleTag<KV<K, Iterable<V>>> mainTag = new TupleTag<>("main output");
+
+    // TODO: remove non-portable operator re-use
+    WindowDoFnOperator<K, V, Iterable<V>> doFnOperator =
+            new WindowDoFnOperator<K, V, Iterable<V>>(
+                    reduceFn,
+                    pTransform.getUniqueName(),
+                    (Coder) windowedWorkItemCoder,
+                    mainTag,
+                    Collections.emptyList(),
+                    new DoFnOperator.MultiOutputOutputManagerFactory(mainTag, 
outputCoder),
+                    windowingStrategy,
+                    new HashMap<>(), /* side-input mapping */
+                    Collections.emptyList(), /* side inputs */
+                    context.getPipelineOptions(),
+                    inputElementCoder.getKeyCoder());
+
+    SingleOutputStreamOperator<WindowedValue<KV<K, Iterable<V>>>> 
outputDataStream =
+            keyedWorkItemStream
+                    .transform(
+                            pTransform.getUniqueName(),
+                            outputTypeInfo,
+                            (OneInputStreamOperator) doFnOperator);
+
+    context.addDataStream(
+            Iterables.getOnlyElement(pTransform.getOutputsMap().values()),
+            outputDataStream);
+  }
+
+  private void translateImpulse(
+          String id,
+          RunnerApi.Pipeline pipeline,
+          StreamingTranslationContext context) {
+    RunnerApi.PTransform pTransform =
+            pipeline.getComponents().getTransformsOrThrow(id);
+
+    TypeInformation<WindowedValue<byte[]>> typeInfo =
+            new CoderTypeInformation<>(
+                    WindowedValue.getFullCoder(ByteArrayCoder.of(), 
GlobalWindow.Coder.INSTANCE));
+
+    DataStreamSource<WindowedValue<byte[]>> source = 
context.getExecutionEnvironment()
+            
.fromCollection(Collections.singleton(WindowedValue.valueInGlobalWindow(
+                    new byte[0])), typeInfo);
+
+    context.addDataStream(
+            Iterables.getOnlyElement(pTransform.getOutputsMap().values()),
+            source);
+  }
+
+  private <T> void translateAssignWindows(String id, RunnerApi.Pipeline 
pipeline,
+                                          StreamingTranslationContext context) 
{
+    RunnerApi.Components components = pipeline.getComponents();
+    RunnerApi.PTransform transform = components.getTransformsOrThrow(id);
+    RunnerApi.WindowIntoPayload payload;
+    try {
+      payload = 
RunnerApi.WindowIntoPayload.parseFrom(transform.getSpec().getPayload());
+    } catch (InvalidProtocolBufferException e) {
+      throw new IllegalArgumentException(e);
+    }
+    //TODO: https://issues.apache.org/jira/browse/BEAM-4296
+    // This only works for well known window fns, we should defer this 
execution to the SDK
+    // if the WindowFn can't be parsed or just defer it all the time.
+    WindowFn<T, ? extends BoundedWindow> windowFn = (WindowFn<T, ? extends 
BoundedWindow>)
+            
WindowingStrategyTranslation.windowFnFromProto(payload.getWindowFn());
+
+    String inputCollectionId = 
Iterables.getOnlyElement(transform.getInputsMap().values());
+    String outputCollectionId =
+            Iterables.getOnlyElement(transform.getOutputsMap().values());
+    Coder<WindowedValue<T>> outputCoder = instantiateCoder(outputCollectionId, 
components);
+    TypeInformation<WindowedValue<T>> resultTypeInfo =
+            new CoderTypeInformation<>(outputCoder);
+
+    DataStream<WindowedValue<T>> inputDataStream = 
context.getDataStreamOrThrow(inputCollectionId);
+
+    FlinkAssignWindows<T, ? extends BoundedWindow> assignWindowsFunction =
+            new FlinkAssignWindows<>(windowFn);
+
+    DataStream<WindowedValue<T>> resultDataStream = inputDataStream
+            .flatMap(assignWindowsFunction)
+            .name(transform.getUniqueName())
+            .returns(resultTypeInfo);
+
+    context.addDataStream(outputCollectionId, resultDataStream);
+  }
+
+  private <InputT, OutputT> void translateExecutableStage(
+          String id,
+          RunnerApi.Pipeline pipeline,
+          StreamingTranslationContext context) {
+
+    throw new RuntimeException("executable stage translation not implemented");
+
+  }
+
+  static <T> Coder<WindowedValue<T>> instantiateCoder(String collectionId,
+                                                      RunnerApi.Components 
components) {
+    PipelineNode.PCollectionNode collectionNode =
+            PipelineNode.pCollection(
+                    collectionId, 
components.getPcollectionsOrThrow(collectionId));
+    try {
+      return WireCoders.instantiateRunnerWireCoder(collectionNode, components);
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+}
diff --git 
a/runners/flink/src/main/java/org/apache/beam/runners/flink/FlinkStreamingTransformTranslators.java
 
b/runners/flink/src/main/java/org/apache/beam/runners/flink/FlinkStreamingTransformTranslators.java
index 59ccecd..1fa9c83 100644
--- 
a/runners/flink/src/main/java/org/apache/beam/runners/flink/FlinkStreamingTransformTranslators.java
+++ 
b/runners/flink/src/main/java/org/apache/beam/runners/flink/FlinkStreamingTransformTranslators.java
@@ -1074,7 +1074,7 @@ class FlinkStreamingTransformTranslators {
     }
   }
 
-  private static class ToKeyedWorkItem<K, InputT>
+  static class ToKeyedWorkItem<K, InputT>
       extends RichFlatMapFunction<
       WindowedValue<KV<K, InputT>>,
       WindowedValue<SingletonKeyedWorkItem<K, InputT>>> {

-- 
To stop receiving notification emails like this one, please contact
[email protected].

Reply via email to