如何使用事务性DatastoreIO

时间:2016-12-10 23:05:08

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

我正在使用来自流式数据流管道的DatastoreIO,并在编写具有相同密钥的实体时收到错误。

2016-12-10T22:51:04.385Z: Error:   (af00222cfd901860): Exception: com.google.datastore.v1.client.DatastoreException: A non-transactional commit may not contain multiple mutations affecting the same entity., code=INVALID_ARGUMENT

如果我在密钥中使用随机数,那么事情可行,但我需要更新相同的密钥,那么是否有使用DataStoreIO执行此操作的事务方法?

static class CreateEntityFn extends DoFn<KV<String, Tile>, Entity> {
  private static final long serialVersionUID = 0;

  private final String namespace;
  private final String kind;

  CreateEntityFn(String namespace, String kind) {
    this.namespace = namespace;
    this.kind = kind;
  }

  public Entity makeEntity(String key, Tile tile) {
    Entity.Builder entityBuilder = Entity.newBuilder();
    Key.Builder keyBuilder = makeKey(kind, key );
    if (namespace != null) {
      keyBuilder.getPartitionIdBuilder().setNamespaceId(namespace);
    }
    entityBuilder.setKey(keyBuilder.build());
    entityBuilder.getMutableProperties().put("tile", makeValue(tile.toString()).build()); 
    return entityBuilder.build();   
  }

  @Override
  public void processElement(ProcessContext c) { 
    String key = c.element().getKey();
    // this works    key = key.concat(":" + UUID.randomUUID().toString());
    c.output(makeEntity(key, c.element().getValue()));
  }
}

...

...
 inputData = pipeline
                .apply(PubsubIO.Read.topic(pubsubTopic));
 windowedDataStreaming = inputData
                .apply(Window.<String>into(
                      SlidingWindows.of(Duration.standardMinutes(15))
                                    .every(Duration.standardSeconds(31))));


                             ...
                             ...
                             ...
 //Create a Datastore entity 
 PCollection<Entity> siteTileEntities = tileSiteKeyed
      .apply(ParDo.named("CreateSiteEntities").of(new CreateEntityFn(options.getNamespace(), options.getKind())));       

 // write site tiles to datastore
 siteTileEntities
      .apply(DatastoreIO.v1().write().withProjectId(options.getDataset()));

 // Run the pipeline
 pipeline.run(); 

2 个答案:

答案 0 :(得分:7)

您的代码段并未解释如何创建tileSiteKeyed。大概它是PCollection<KV<String, Tile>,但如果它可能有重复的String键,那就可以解释这个问题。

通常,PCollection<KV<K, V>>可能包含多个具有相同密钥的KV对。如果您想确保每个窗口的唯一键,可以使用GroupByKey来执行此操作。这将为您提供每个窗口具有唯一键的PCollection<KV<K, Iterable<V>>>。然后扩充CreateEntityFn以获取Iterable<Tile>并使用您需要进行的更改创建单个突变。

答案 1 :(得分:2)

此错误表示Cloud Datastore收到Commit个请求,其中有两个针对同一个密钥的突变(即它尝试两次插入同一个实体或两次修改同一个实体)。

您可以通过每Commit个请求仅包含一个突变来避免错误。