如何捕获BigQueryIO.Write引发的任何异常并抢救无法输出的数据?

时间:2017-12-28 05:35:07

标签: google-bigquery google-cloud-dataflow apache-beam

我想从Cloud Pub / Sub读取数据并使用Cloud Dataflow将其写入BigQuery。每个数据都包含一个表ID,其中将保存数据本身。

写入BigQuery的各种因素都失败了:

  • 表格ID格式错误。
  • 数据集不存在。
  • 数据集不允许管道访问。
  • 网络故障。

当其中一个失败发生时,流式传输作业将重试该任务并停止。我尝试使用WriteResult.getFailedInserts()来挽救不良数据并避免拖延,但是效果不佳。有什么好办法吗?

这是我的代码:

public class StarterPipeline {
  private static final Logger LOG = LoggerFactory.getLogger(StarterPipeline.class);

  public class MyData implements Serializable {
    String table_id;
  }

  public interface MyOptions extends PipelineOptions {
    @Description("PubSub topic to read from, specified as projects/<project_id>/topics/<topic_id>")
    @Validation.Required
    ValueProvider<String> getInputTopic();
    void setInputTopic(ValueProvider<String> value);
  }

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

    Pipeline p = Pipeline.create(options);

    PCollection<MyData> input = p
        .apply("ReadFromPubSub", PubsubIO.readStrings().fromTopic(options.getInputTopic()))
        .apply("ParseJSON", MapElements.into(TypeDescriptor.of(MyData.class))
            .via((String text) -> new Gson().fromJson(text, MyData.class)));
    WriteResult writeResult = input
        .apply("WriteToBigQuery", BigQueryIO.<MyData>write()
            .to(new SerializableFunction<ValueInSingleWindow<MyData>, TableDestination>() {
              @Override
              public TableDestination apply(ValueInSingleWindow<MyData> input) {
                MyData myData = input.getValue();
                return new TableDestination(myData.table_id, null);
              }
            })
            .withSchema(new TableSchema().setFields(new ArrayList<TableFieldSchema>() {{
              add(new TableFieldSchema().setName("table_id").setType("STRING"));
            }}))
            .withFormatFunction(new SerializableFunction<MyData, TableRow>() {
              @Override
              public TableRow apply(MyData myData) {
                return new TableRow().set("table_id", myData.table_id);
              }
            })
            .withCreateDisposition(BigQueryIO.Write.CreateDisposition.CREATE_IF_NEEDED)
            .withWriteDisposition(BigQueryIO.Write.WriteDisposition.WRITE_APPEND)
            .withFailedInsertRetryPolicy(InsertRetryPolicy.neverRetry()));
    writeResult.getFailedInserts()
        .apply("LogFailedData", ParDo.of(new DoFn<TableRow, TableRow>() {
          @ProcessElement
          public void processElement(ProcessContext c) {
            TableRow row = c.element();
            LOG.info(row.get("table_id").toString());
          }
        }));

    p.run();
  }
}

1 个答案:

答案 0 :(得分:4)

在管道定义中写入输出时,没有简单的方法来捕获异常。我想你可以通过为BigQuery编写自定义PTransform来实现。但是,没有办法在Apache Beam中本地执行此操作。我还建议不要这样做,因为它会破坏Cloud Dataflow的自动重试功能。

在您的代码示例中,您将失败的插入重试策略设置为永不重试。您可以将策略设置为始终重试。这仅在间歇性网络故障(第4个要点)期间有效。

.withFailedInsertRetryPolicy(InsertRetryPolicy.alwaysRetry())

如果表格ID格式不正确(第1个项目符号),则CREATE_IF_NEEDED创建处置配置应允许数据流作业自动创建新表而不会出错,即使表ID不正确。

如果数据集不存在或者数据集存在访问权限问题(第2和第3个项目符号),那么我认为流媒体作业应该停止并最终失败。在没有人工干预的情况下,无法在任何情况下继续进行。