PubSub到Spanner流传输管道

时间:2019-10-08 05:19:54

标签: google-cloud-dataflow google-cloud-pubsub google-cloud-spanner

我正在尝试将JSON类型的PubSub消息流式传输到扳手数据库,并且insert_update效果很好。 Spanner表具有复合主键,因此需要在从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;

}

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

1 个答案:

答案 0 :(得分:0)

我不确定您在做什么,但是您似乎想要:

  • 使用与pubsub消息匹配的键(或部分键)读取一些现有数据
  • 删除此数据
  • 从pubsub消息中插入新数据

一个选项是创建一个DoFn,该操作在读写事务中执行此读取/删除/插入(或读取/更新)。这样可以保持数据库的一致性...

使用SpannerIO.WriteFn作为模型-您需要将SpannerAccessor设置为瞬态,并在@Setup@Teardown事件处理程序中创建/删除它

您的@ProcessElement的{​​{1}}处理程序将创建一个Read-write Transaction,在其中您将读取密钥的行,对其进行更新或删除,然后插入新元素。

此方法的缺点是,每个Spanner事务将仅处理一条Pub / Sub消息(除非您在上一步中进行了巧妙的操作(例如将它们分组)),这是一个复杂的读写事务。如果您的消息/秒速率相对较低,则可以,但是,如果不这样,则此方法将给数据库增加更多的负载。

第二种选择是对键范围进行盲删除。仅当object_id是复合键的第一部分(它似乎来自您的代码)时,此方法才有效。

您将创建一个包含删除突变的DoFn,该突变使用具有键范围的Delete突变来盲删除其键以object_id开头的所有现有行,然后使用insert突变来替换已删除的行。 / p>

MutationGroup

然后将其像以前一样传递给SpannerIO.write()。grouped(),以便可以提高效率。