来自Beam PTransform

时间:2017-06-05 21:33:14

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

这是我写的Apache Beam PTransform:

public class NormalizeTransform
  extends PTransform<PCollection<String>, PCollection<SimpleTable>> {

@Override
public PCollection<SimpleTable> expand(PCollection<String> lines) {
  ExtractFields extract_i = new ExtractFields();
  PCollection<SimpleTable> table = lines
    .apply("Extracting data model fields from lines",
           ParDo.of(extract_i));
}                                                   

public class ExtractFields extends DoFn<String, SimpleTable> {

@ProcessElement
public void processElement(ProcessContext c){
  try {
    String line = c.element();              
    // fill table
    for (Table_Struct st: this.struct){
      String o = line.substring(st.pos_1, st.pos_2));
      this.table.getClass().getField(st.Field_Name).set(
        this.table, o);                                                                     
    }
    c.output(this.table);
  }
}

偶尔我会得到以下错误IllegalMutationException,这意味着我重复了代码的运行,有时它会起作用,有时它不会。

org.apache.beam.sdk.util.IllegalMutationException: PTransform Transform/Extracting data model fields from lines/ParMultiDo(ExtractFields) mutated value  after it was output (new value was ). Values must not be mutated in any way after being output.

at org.apache.beam.runners.direct.ImmutabilityCheckingBundleFactory$ImmutabilityEnforcingBundle.commit(ImmutabilityCheckingBundleFactory.java:135)
at org.apache.beam.runners.direct.EvaluationContext.commitBundles(EvaluationContext.java:214)
at org.apache.beam.runners.direct.EvaluationContext.handleResult(EvaluationContext.java:163)
at org.apache.beam.runners.direct.ExecutorServiceParallelExecutor$TimerIterableCompletionCallback.handleResult(ExecutorServiceParallelExecutor.java:268)
at org.apache.beam.runners.direct.TransformExecutor.finishBundle(TransformExecutor.java:168)
at org.apache.beam.runners.direct.TransformExecutor.run(TransformExecutor.java:109)
at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)

我不认为我在代码中的任何位置都特意更改了值的任何输出。 MutationDetectors将比较两个值:previousValue和newValue。在我的例子中,previousValue通常是一个输入值,newValue是另一个输入值。为什么Transform会尝试使用一个输入值来修改另一个输入值?

2 个答案:

答案 0 :(得分:2)

我不确定this.table来自哪里。

但为了帮助您理解错误消息,请记住,可能会在多个输入上调用processElement。第一个调用将输出this.table。在输出之前,下一个调用将改变this.table

如果在第一次调用输出this.table之后且下游代码有机会读取this.table之前发生此突变,则结果不正确。所以,这个错误表明你在输出引用后已经改变了this.table的内容 - 这是你不应该做的。

请考虑(1)输出this.table的副本或(2)将表创建为本地字段。例如:

@ProcessElement
public void processElement(ProcessContext c){
  try {
    String line = c.element(); 
    Table table = /* create the table */;             
    // fill table
    for (Table_Struct st: this.struct){
      String o = line.substring(st.pos_1, st.pos_2));
      this.table.getClass().getField(st.Field_Name)
        .set(table, o);                                                                     
    }
    c.output(table);
  }
}

另请注意,在每个processElement内执行反射可能比所需的慢。如果您可以直接修改字段,那可能会更好。

答案 1 :(得分:0)

还请注意,强制执行会比较反序列化的值,因此假定反序列化是确定性的。如果对象包含集合,则可能需要注意元素的顺序。