协议缓冲区如何处理版本控制?

时间:2011-12-15 11:30:10

标签: protocol-buffers protobuf-net

协议缓冲区如何处理类型版本控制?

例如,当我需要随时间更改类型定义时?就像添加和删除字段一样。

2 个答案:

答案 0 :(得分:22)

谷歌设计的protobuf对版本化非常宽容:

  • 意外数据存储为“扩展”(使其成为往返安全),或者以静默方式删除,具体取决于实现
  • 新字段通常添加为“可选”,表示可以成功加载旧数据

但是:

  • 不会重新编号字段 - 这会破坏现有数据
  • 你通常不应该改变任何给定字段的存储方式(即从固定的32位int到“varint”)

一般来说,虽然 - 它只是工作,你不需要担心版本控制。

答案 1 :(得分:5)

我知道这是一个老问题,但最近我遇到了这个问题。我绕过它的方式是使用外观和运行时决定序列化。通过这种方式,我可以将字段弃用/升级为新类型,旧消息和新消息可以正常处理它。

我正在使用Marc Gravell的protobuf.net(v2.3.5)和C#,但外墙理论适用于任何语言和Google原始的protobuf实现。

我的旧类有一个DateTime的时间戳,我想改变它以包含“Kind”(一个.NET时代错误)。有效地添加它意味着它被序列化为9个字节而不是8个字节,这将是一个破坏序列化的变化!

    [ProtoMember(3, Name = "Timestamp")]
    public DateTime Timestamp { get; set; }

protobuf的基础是永远不要改变原型!我想阅读旧的序列化二进制文件,这意味着“3”就在这里。

所以,

我重命名了旧属性并将其设为私有(是的,它仍然可以通过反射魔法反序列化),但我的API不再显示它可用!

    [ProtoMember(3, Name = "Timestamp-v1")]
    private DateTime __Timestamp_v1 = DateTime.MinValue;

我创建了一个新的Timestamp属性,带有一个新的proto id,并包含了DateTime.Kind

    [ProtoMember(30002, Name = "Timestamp", DataFormat = ProtoBuf.DataFormat.WellKnown)]
    public DateTime Timestamp { get; set; }

我添加了一个“AfterDeserialization”方法来更新我们的新时间,如果是旧消息

    [ProtoAfterDeserialization]
    private void AfterDeserialization()
    {
        //V2 Timestamp includes a "kind" - we will stop using __Timestamp - so keep it up to date
        if (__Timestamp_v1 != DateTime.MinValue)
        {
            //Assume the timestamp was in UTC - as it was...
            Timestamp = new DateTime(__Timestamp_v1.Ticks, DateTimeKind.Utc)     //This is for old messages - we'll update our V2 timestamp...
        }
    }

现在,我正确地序列化/反序列化旧消息和新消息,我的Timestamp现在包含DateTime.Kind!什么都没破。

然而,这确实意味着BOTH字段将在所有新消息中继续存在。因此,最后一步是使用运行时序列化决策来排除旧的时间戳(请注意,如果使用protobuf的必需属性,这将无法工作!!!)

    bool ShouldSerialize__Timestamp_v1() 
    {
        return __Timestamp_v1 != DateTime.MinValue;
    }

就是这样。如果有人想要的话,我有一个很好的单元测试,它可以从头到尾完成...

我知道我的方法依赖于.NET魔法,但我认为这个概念可以翻译成其他语言....