Java 束流传输的非法突变异常
这是我写的Apache Beam PTransform:Java 束流传输的非法突变异常,java,google-cloud-dataflow,apache-beam,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) { ExtractF
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);
}
}
我不认为我在代码中的任何地方专门更改了值的任何输出。突变检测器将比较两个值:previousValue和newValue。在我的例子中,previousValue通常是一个输入值,newValue是另一个输入值。为什么转换会尝试使用一个输入值来修改另一个输入值?我不确定这个表是从哪里来的 但是为了帮助您理解错误消息,请记住,
processElement
可以在多个输入上调用。第一次调用将输出this.table
。下一次调用将在输出此表之前对其进行变异
如果此变异发生在第一次调用输出了this.table
之后,并且下游代码还没有机会读取this.table
之前,那么您将得到不正确的结果。因此,此错误表示您在输出引用后对this.table
的内容进行了变异,这是您不应该做的
请考虑(1)输出此.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
中执行反射可能比期望的慢。如果可以直接修改字段,则可能会更好。还要注意,强制比较反序列化的值,因此它假定反序列化是确定性的。如果对象包含集合,则需要注意元素的顺序。非常感谢您的回复。这就解释了原因。实际上,我确实担心processElement
中的反射可能会拖累性能。但在这种情况下,表是运行时定义的,不能直接修改,因此我求助于使用反射。你有比反射更好的主意吗?因为它是在运行时定义的,所以除了反射之外,你没有其他的选择。这取决于您可以使用哪些其他选项/库来避免反射。您对其他选项/库有何建议?感谢@Ben Chambers,其他选项包括使用AutoValue、Avro或协议缓冲区从模式生成代码,以及使用它们。很大程度上取决于您的实际用例是什么。您可能需要更详细地描述您正在尝试做什么,并提出一个更一般的Java问题。
@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);
}
}