谷歌数据流管道中的数据存储输入是否可以一次处理一批N个条目?

时间:2016-01-28 15:10:50

标签: google-cloud-dataflow google-cloud-datastore gcloud dataflow

我正在尝试执行数据流管道作业,该作业将从数据存储区一次 N个条目执行一个功能。在我的情况下,此函数将一批100个条目作为有效负载发送到某些REST服务。这意味着我想要查看来自一个数据存储区实体的所有条目,并将 100个批处理条目一次发送到某些外部REST服务。

我目前的解决方案

  1. 从数据存储中读取输入
  2. 创建与管道选项中指定的工作者一样多的键(1 worker = 1键)。
  3. 按键分组,以便我们将迭代器作为输出(步骤4中的迭代器输入)
  4. 以编程方式批处理临时列表中的用户,并将其作为批处理发送到REST端点。
  5. 上面描述的伪代码场景(忽略细节):

    final int BATCH_SIZE = 100;
    
    // 1. Read input from datastore
    pipeline.apply(DatastoreIO.readFrom(datasetId, query))
    
        // 2. create keys to be used in group by so we get iterator in next task
        .apply(ParDo.of(new DoFn<DatastoreV1.Entity, KV<String, EntryPOJO>>() {
            @Override
            public void processElement(ProcessContext c) throws Exception {
                String key = generateKey(c);
                EntryPOJO entry = processEntity(c);
                c.output(KV.of(key, entry));
            }
        }))
    
        // 3. Group by key
        .apply(GroupByKey.create())
    
        // 4. Programatically batch users
        .apply(ParDo.of(new DoFn<KV<String, Iterable<EntryPOJO>>() {
            @Override
            public void processElement(ProcessContext c) throws Exception {
                List<EntryPOJO> batchedEntries = new ArrayList<>();
                for (EntryPOJO entry : c.element().getValue()) {
                    if (batchedEntries.size() >= BATCH_SIZE) {
                        sendToRESTEndpoint(batchedEntries);
                        batchedEntries = new ArrayList<>();
                    }
                    batchedEntries.add(entry);
                }
                sendToRESTEndpoint(batchedEntries);
            }
        }));
    

    我当前解决方案的主要问题

    GroupByKey阻止执行最后一个ParDo(块步骤4),直到所有条目都分配给一个键。

    解决方案通常有效,但我想并行执行所有操作(从数据存储加载后立即将批量的100个条目发送到REST端点),这对于我当前的解决方案是不可能的因为GroupByKey不会输出任何数据,直到获取数据库中的每个条目并将其插入键值对。因此,执行实际上分为两步:1。从数据存储中获取所有数据并为其分配密钥,2。批处理条目

    问题

    所以我想知道的是,是否有一些现有功能可以做到这一点。或者至少在没有GroupByKey步骤的情况下获得Iterable,这样批处理功能任务就不需要等待数据被转储。

1 个答案:

答案 0 :(得分:6)

您可以在DoFn内批量处理这些元素。例如:

final int BATCH_SIZE = 100;

pipeline
  // 1. Read input from datastore  
  .apply(DatastoreIO.readFrom(datasetId, query))

  // 2. Programatically batch users
  .apply(ParDo.of(new DoFn<DatastoreV1.Entity, Iterable<EntryPOJO>>() {

    private final List<EntryPOJO> accumulator = new ArrayList<>(BATCH_SIZE);

    @Override
    public void processElement(ProcessContext c) throws Exception {
      EntryPOJO entry = processEntity(c);
      accumulator.add(c);
      if (accumulator.size() >= BATCH_SIZE) {
        c.output(accumulator);
        accumulator = new ArrayList<>(BATCH_SIZE);
      }
    }

    @Override
    public void finishBundle(Context c) throws Exception {
      if (accumulator.size() > 0) {
        c.output(accumulator);
      }
    }
  });

  // 3. Consume those bundles
  .apply(ParDo.of(new DoFn<Iterable<EntryPOJO>, Object>() {
    @Override
    public void processElement(ProcessContext c) throws Exception {
        sendToRESTEndpoint(batchedEntries);
    }
  }));

如果您不想单独进行“批处理”步骤,也可以在单个DoFn中合并第2步和第3步。