为什么GroupByKey在光束管道中复制元素(在Google Dataflow上运行时)?

时间:2017-10-11 13:17:04

标签: google-cloud-dataflow apache-beam

背景

我们有一个管道,它从接收来自PubSub的消息开始,每个消息都有一个文件的名称。这些文件被分解为行级别,解析为JSON对象节点,然后发送到外部解码服务(对某些编码数据进行解码)。对象节点最终转换为Table Rows并写入Big Query。

似乎Dataflow在他们到达解码服务之前没有确认PubSub消息。解码服务很慢,导致一次发送许多消息时出现积压。这意味着与PubSub消息相关联的行可能需要一些时间才能到达解码服务。结果,PubSub没有收到确认并重新发送消息。我第一次尝试解决这个问题是为每个使用withAttributeId()传递给Reader的PubSub消息添加一个属性。但是,在测试时,这只能防止紧密相连的重复。

我的第二次尝试是在PubSub阅读后添加fusion breakerexample)。这只是执行一个不必要的GroupByKey然后取消组合,这个想法是GroupByKey强制Dataflow确认PubSub消息。

问题

上面讨论的融合破坏程序的作用是防止PubSub重新发送消息,但我发现这个GroupByKey输出的元素多于它接收的元素See image

为了尝试和诊断这个,我删除了部分管道以获得仍然表现出这种行为的简单管道。

时,行为仍然存在
  • PubSub被一些虚拟变换所取代,这些虚拟变换发送一个固定的消息列表,每个消息之间稍有延迟。
  • 删除了写入转换。
  • 删除所有侧输入/输出。

我观察到的行为是:

  1. 一些收到的消息直接通过GroupByKey。
  2. 在某一点之后,消息被保持在'通过GroupByKey(可能是由于GroupByKey之后的积压)。
  3. 这些消息最终会退出GroupByKey(大小为1的组)。
  4. 在短暂延迟(大约3分钟)之后,相同的消息再次退出GroupByKey(仍然是大小为1的组)。这可能会发生几次(我怀疑它与他们等待进入GroupByKey的时间成正比)。
  5. 示例作业ID为2017-10-11_03_50_42-6097948956276262224。我没有对任何其他跑步者跑光束。

    Fusion Breaker如下:

    @Slf4j
    public class FusionBreaker<T> extends PTransform<PCollection<T>, PCollection<T>> {
    
      @Override
      public PCollection<T> expand(PCollection<T> input) {
        return group(window(input.apply(ParDo.of(new PassthroughLogger<>(PassthroughLogger.Level.Info, "Fusion break in")))))
                .apply("Getting iterables after breaking fusion", Values.create())
                .apply("Flattening iterables after breaking fusion", Flatten.iterables())
                .apply(ParDo.of(new PassthroughLogger<>(PassthroughLogger.Level.Info, "Fusion break out")));
      }
    
      private PCollection<T> window(PCollection<T> input) {
        return input.apply("Windowing before breaking fusion", Window.<T>into(new GlobalWindows())
                .triggering(Repeatedly.forever(AfterPane.elementCountAtLeast(1)))
                .discardingFiredPanes());
      }
    
      private PCollection<KV<Integer, Iterable<T>>> group(PCollection<T> input) {
        return input.apply("Keying with random number", ParDo.of(new RandomKeyFn<>()))
                .apply("Grouping by key to break fusion", GroupByKey.create());
      }
    
      private static class RandomKeyFn<T> extends DoFn<T, KV<Integer, T>> {
        private Random random;
    
        @Setup
        public void setup() {
          random = new Random();
        }
    
        @ProcessElement
        public void processElement(ProcessContext context) {
          context.output(KV.of(random.nextInt(), context.element()));
        }
      }
    
    }
    

    PassthroughLoggers只记录通过的元素(我使用这些来确认元素确实重复,而不是计数有问题)。

    我怀疑这与windows /触发器有关,但我的理解是,当使用.discardingFiredPanes()时,不应重复元素 - 无论窗口设置如何。我也试过FixedWindows但没有成功。

1 个答案:

答案 0 :(得分:1)

首先,Reshuffle转换相当于您的Fusion Breaker,但还有一些额外的性能改进,应该更适合。

其次,如果重试,计数器和日志记录可能会多次看到一个元素。如Beam Execution Model中所述,如果重试融入的任何内容,则可以重试步骤中的元素。

您是否真的在写入管道输出的内容中发现了重复项?