如果字段顺序更改,则 Avro 架构不兼容

Abh*_*nan 7 java schema avro

场景 - 客户端使用 Avro Reflect Datum Writer 序列化 POJO,并将 GenericRecord 写入文件。通过反射获得的模式是这样的(注意顺序A,B,D,C) -

{
"namespace": "storage.management.example.schema",

"type": "record",
"doc": "Example schema for testing",
"name": "Event",
"fields": [
     ....
     ....
    { "name": "A", "type":  "string"  },
    { "name": "B", "type":  "string"  },
    { "name": "D", "type": "string" },
    { "name": "C", "type":  "string"  },
     ....
     ....
]
} 
Run Code Online (Sandbox Code Playgroud)

代理读取文件并使用默认模式(注意顺序 - A、B、C、D)反序列化记录的子集(客户端保证具有这些字段)

{
"namespace": "storage.management.example.schema",
"type": "record",
"doc": "Example schema for testing",
"name": "Event",
"fields": [
    { "name": "A", "type":  "string"  },
    { "name": "B", "type":  "string"  },
    { "name": "C", "type": "string" },
    { "name": "D", "type":  "string"  }
]
}
Run Code Online (Sandbox Code Playgroud)

问题:使用上述子集模式进行反序列化会导致以下异常 -

Caused by: java.io.IOException: Invalid int encoding
at org.apache.avro.io.BinaryDecoder.readInt(BinaryDecoder.java:145)
at org.apache.avro.io.BinaryDecoder.readString(BinaryDecoder.java:259)
at org.apache.avro.io.ResolvingDecoder.readString(ResolvingDecoder.java:201)
at org.apache.avro.generic.GenericDatumReader.readString(GenericDatumReader.java:430)
at org.apache.avro.generic.GenericDatumReader.readString(GenericDatumReader.java:422)
at org.apache.avro.generic.GenericDatumReader.readWithoutConversion(GenericDatumReader.java:180)
at org.apache.avro.generic.GenericDatumReader.read(GenericDatumReader.java:152)
at org.apache.avro.generic.GenericDatumReader.readField(GenericDatumReader.java:240)
at org.apache.avro.generic.GenericDatumReader.readRecord(GenericDatumReader.java:230)
Run Code Online (Sandbox Code Playgroud)

但是,如果子集模式也按 A、B、D、C 的顺序指定字段,则反序列化成功。(与客户端模式相同)

这种行为是预期的吗?我虽然 Avro 只依赖于字段名称来构建记录而不是排序。

对此有任何修复吗?不同的客户端可能有不同的顺序,我无法强制执行顺序,因为架构是通过反射生成的。

Cap*_*Man 7

这不一定是预期的行为。当我开始使用 Avro 时,您可能会犯同样的错误。

Avro 能够拥有不同版本的模式(例如,用一种写入但读入另一种),但是很容易错过的一件事(至少我自己)是您必须拥有在尝试阅读消息时写入消息确切模式。

您阅读的有关 Avro 的文档和信息,至少在表面层面,并没有说得很清楚。通常他们专注于“向后兼容”。公平地说,它在某种意义上是这样的,但通常当人们看到这个短语时,他们认为它的意思有点不同。通常我们认为这意味着您可以使用新模式处理旧消息,而不是使用新模式和旧消息模式处理旧消息。

例如,请参阅此伪代码

Schema myUnsortedSchema has C B A order
Schema myAlphabeticalSchema has A B C order

Writer writer uses myUnsortedSchema
Reader badReader uses myAlphabeticalSchema only

writer writes message
badReader reads message
Run Code Online (Sandbox Code Playgroud)

错误!不确定错误消息会准确表达什么,但问题是badReader不仅尝试读入myAlphabeticalSchema而且读取消息,就好像它是由myAlphabeticalSchema. 解决方案是有一种方法可以为它提供两种模式,即写入消息的模式和要读入的模式(如何取决于语言)。

Reader goodReader reads messages written with myUnsortedSchema into myAlphabeticalSchema

goodReader reads message
Run Code Online (Sandbox Code Playgroud)

没有错误!这是正确的用法。

如果您使用类似的方法,goodReader则此行为是意外的,但如果您使用类似的方法,badReader则该行为是预期的。


像 Schema Registry 这样的一些服务通过在消息字节的前面附加一些元数据来确定哪个模式写入了消息(当然在阅读之前将它们剥离)来帮助解决这个问题。这超出了问题的范围,但可以帮助解决此类问题。


小智 1

这种行为是预期的吗?

文档说“记录是通过按照声明的顺序对其字段的值进行编码来编码的。

所以,我认为这是正确的行为。