Google cloud dataflow PubSub至扳手流式管道

Google cloud dataflow PubSub至扳手流式管道,google-cloud-dataflow,google-cloud-pubsub,google-cloud-spanner,Google Cloud Dataflow,Google Cloud Pubsub,Google Cloud Spanner,我正在尝试将类型为JSON的PubSub消息流式传输到Spaner数据库,insert_更新工作得非常好。 扳手表具有复合主键,因此需要在从PubSub插入新数据之前删除现有数据(因此仅存在最新数据)。扳手更换或插入/更新突变在这种情况下不起作用。 我添加了管道 import org.apache.beam.* ; public class PubSubToSpannerPipeline { // JSON to TableData Object public static cla

我正在尝试将类型为JSON的PubSub消息流式传输到Spaner数据库,insert_更新工作得非常好。 扳手表具有复合主键,因此需要在从PubSub插入新数据之前删除现有数据(因此仅存在最新数据)。扳手更换或插入/更新突变在这种情况下不起作用。 我添加了管道


import org.apache.beam.* ;

public class PubSubToSpannerPipeline {

  // JSON to TableData Object
  public static class PubSubToTableDataFn extends DoFn<String, TableData> {

    @ProcessElement
    public void processElement(ProcessContext c) {
      .
      .
      .
    }
  }

  public interface PubSubToSpannerOptions extends PipelineOptions, StreamingOptions {
    .
    .
    .
  }

  public static void main(String[] args) {
    PubSubToSpannerOptions options = PipelineOptionsFactory
        .fromArgs(args)
        .withValidation()
        .as(PubSubToSpannerOptions.class);
    options.setStreaming(true);

    SpannerConfig spannerConfig =
        SpannerConfig.create()
        .withProjectId(options.getProjectId())
        .withInstanceId(options.getInstanceId())
        .withDatabaseId(options.getDatabaseId());

    Pipeline pipeLine = Pipeline.create(options);

    PCollection<TableData> tableDataMsgs = pipeLine.apply(PubsubIO.readStrings()
        .fromSubscription(options.getInputSubscription()))
        .apply("ParsePubSubMessage", ParDo.of(new PubSubToTableDataFn ()));

    // Window function
    PCollection<TableData> tableDataJson = tableDataMsgs
        .apply(Window.into(FixedWindows.of(Duration.standardMinutes(1))));

    PCollection<MutationGroup> upsertMutationGroup = tableDataJson.apply("TableDataMutation",
        MapElements.via(new SimpleFunction<TableData, MutationGroup>() {

          public MutationGroup apply(TableData input) {

            String object_id = input.objectId;

            pipeLine.apply("ReadExistingData", SpannerIO.read()
                .withSpannerConfig(spannerConfig)
                .withQuery("SELECT object_id, mapped_object_id, mapped_object_name from TableName where object_id ='" + object_id + "'")
            .apply("MutationForExistingTableData", 
                    ParDo.of(new DoFn<Struct, Mutation>(){
                      @ProcessElement
                      public void processElement(ProcessContext c) {
                        Struct str = c.element();
                        c.output(Mutation.delete("TableName", KeySet.newBuilder()
                            .addKey(Key.newBuilder()
                                .append(str.getString("object_id"))
                                .append(str.getString("mapped_object_id"))
                                .append(str.getString("mapped_object_name")).build()).build()));
                      }
                    } ))
            .apply("DeleteExistingTableData", SpannerIO.write().withSpannerConfig(spannerConfig));

              Mutation dataMutation = Mutation.newReplaceBuilder("TableName",
                  .
                  .
                  .

                  );
              List<Mutation> list = new ArrayList<Mutation>();


              List<Map<String, String>> mappingList = input.listOfObjectRows;

              for (Map<String, String> objectMap : mappingList ) {
                list.add(Mutation.newReplaceBuilder("TableName",
                    .
                    .
                    .);
              }     

              return MutationGroup.create(dataMutation, list);


          }
        } )));


        upsertMutationGroup.apply("WriteDataToSpanner", SpannerIO.write()
            .withSpannerConfig(spannerConfig)
            .grouped());

        // Run the pipeline.
        pipeLine.run().waitUntilFinish();
  }

}

class TableData implements Serializable {
  String objectId;
  List<Map<String, String>> listOfObjectRows;

}

导入org.apache.beam.*;
公共类公用管道{
//JSON到TableData对象
公共静态类PubSubToTableDataFn扩展了DoFn{
@过程元素
公共void processElement(ProcessContext c){
.
.
.
}
}
公共接口PubSubProptions扩展了PipelineOptions、StreamingOptions{
.
.
.
}
公共静态void main(字符串[]args){
PubSubProptions选项=管道选项工厂
.fromArgs(args)
.withValidation()
.as(PubSubProptions.class);
选项。设置流(true);
扳手配置扳手配置=
span配置.create()
.withProjectId(options.getProjectId())
.withInstanceId(options.getInstanceId())
.withDatabaseId(options.getDatabaseId());
Pipeline=Pipeline.create(选项);
PCollection tableDataMsgs=pipeLine.apply(PubsubIO.readStrings()
.fromSubscription(options.getInputSubscription())
.apply(“ParsePubSubMessage”,ParDo.of(new PubSubToTableDataFn()));
//窗口函数
PCollection tableDataJson=tableDataMsgs
.apply(Window.into(fixed windows.of(Duration.standard minutes(1)));
PCollection upsertMutationGroup=tableDataJson.apply(“TableDataMutation”,
via(新的SimpleFunction(){
公共变量组应用(TableData输入){
String object_id=input.objectId;
apply(“ReadExistingData”,span.read()
.WissPannerConfig(扳手配置)
.withQuery(“从TableName中选择对象id、映射对象id、映射对象名称,其中对象id=”+“对象id+”))
.apply(“MutationForExistingTableData”,
(新DoFn()的副部长{
@过程元素
公共void processElement(ProcessContext c){
str=c.element();
c、 输出(Mutation.delete(“TableName”),KeySet.newBuilder()
.addKey(Key.newBuilder()
.append(str.getString(“object_id”))
.append(str.getString(“映射的对象id”))
.append(str.getString(“mapped_object_name”).build()).build());
}
} ))
.apply(“DeleteExistingTableData”,span.write().WithPannerConfig(span配置));
突变数据突变=突变.newReplaceBuilder(“TableName”,
.
.
.
);
列表=新的ArrayList();
List mappingList=input.listOfObjectRows;
用于(映射对象映射:映射列表){
list.add(Mutation.newReplaceBuilder(“TableName”),
.
.
.);
}     
返回MutationGroup.create(dataMutation,list);
}
} )));
upsertMutationGroup.apply(“writeDatatosPaner”,span.write()
.WissPannerConfig(扳手配置)
.分组());
//运行管道。
pipeLine.run().waitUntilFinish();
}
}
类TableData实现了可序列化{
字符串objectId;
对象行列表;
}

预期是在插入或更新数据之前必须从表中删除现有映射数据。

我不完全确定您在做什么,但看起来您希望:

  • 使用与pubsub消息匹配的密钥(或部分密钥)读取一些现有数据
  • 删除此数据
  • 从pubsub消息插入新数据
一个选项是创建一个在读写事务中执行读/删除/插入(或读/更新)的
DoFn

将用作模型-您需要将
扳手访问器设置为瞬态,并在
@Setup
@Teardown
事件处理程序中创建/删除它

DoFn
@ProcessElement
处理程序将创建一个,您将在其中读取键的行,更新或删除它们,然后插入新元素

这种方法的缺点是每个扳手事务只处理一条Pub/Sub消息(除非您在前面的步骤中做了一些聪明的事情,例如对它们进行分组),这是一个复杂的读写事务。如果您的消息/秒速率相对较低,这将很好,但如果不是,此方法将给数据库带来更多负载

第二种选择是使用密钥范围的盲删除。这仅在对象id是复合密钥的第一部分(它似乎来自您的代码)时才有效

您将创建一个
MutationGroup
,其中包含一个delete-mutation,它使用一个键范围的delete-mutation盲删除键以object\u id开头的任何现有行,然后使用insert-mutation替换删除的行

MutationGroup.create(
//删除键以object_id开头的行。
delete(“TableName”,KeySet.newBuilder()
.addRange(
KeyRange.closedClosed(
Key.of(str.getString(“object_id”),
Key.of(str.getString(“object_id”))
.build()),
//插入替换行。
newInsertBuilder(“表名”)
。将(“列”)设置为(“值”),
...
.build(),
newInsertBuilder(“表名”)
...);
这就是p