web-dev-qa-db-ja.com

プロトコルバッファはバージョン管理をどのように処理しますか?

プロトコルバッファーは型のバージョン管理をどのように処理しますか?

たとえば、時間の経過とともに型定義を変更する必要がある場合はどうなりますか?フィールドの追加や削除のように。

35
CodingHero

Googleはprotobufをバージョン管理にかなり寛容になるように設計しました:

  • 予期しないデータは、実装に応じて、「拡張機能」として保存される(往復安全にする)か、サイレントにドロップされる
  • 新しいフィールドは通常、「オプション」として追加されます。つまり、古いデータを正常にロードできます。

しかしながら:

  • しないrenumber fields-それは既存のデータを壊します
  • 通常、特定のフィールドの格納方法を変更しないでください(つまり、32ビットの固定整数から「varint」に)。

ただし、一般的に言えば、それは動作するだけであり、バージョン管理についてそれほど心配する必要はありません。

25
Marc Gravell

これは古い質問であることは知っていますが、最近この問題に遭遇しました。私がそれを回避した方法は、ファサードを使用し、シリアル化する実行時の決定です。このようにして、古いメッセージと新しいメッセージが適切に処理するように、フィールドを非推奨/新しいタイプにアップグレードできます。

私はMarc Gravellのprotobuf.net(v2.3.5)とC#を使用していますが、ファサードの理論はどの言語でも機能し、Googleのオリジナルのprotobuf実装でも機能します。

私の古いクラスにはDateTimeのTimestampがありましたが、「Kind」(。NETの時代遅れ)を含めるように変更しました。これを追加すると、8バイトではなく9バイトにシリアル化されることになります。これは、シリアル化の重大な変更です。

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

Protobufの基本は、決してproto idを変更しないことです!私は古いシリアル化されたバイナリを読みたかったので、「3」が残っていることを意味しました。

そう、

古いプロパティの名前を変更してプライベートにしました(そうです、リフレクションマジックを介して逆シリアル化できます)が、私のAPIはそれを使用できることを示していません。

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

新しいプロトコルIDで新しいTimestampプロパティを作成し、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が含まれるようになりました。何も壊れていません。

ただし、これは両方のフィールドが今後のすべての新しいメッセージに含まれることを意味します。したがって、最後のタッチは、ランタイムシリアライゼーション決定を使用して古いタイムスタンプを除外することです(protobufの必須属性を使用している場合、これは機能しません!!!)

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

以上です。私は、誰かがそれを望んでいる場合にエンドツーエンドでそれを行う素晴らしいユニットテストを持っています...

私の方法は.NETマジックに依存していることはわかっていますが、この概念は他の言語に翻訳できると考えています。

7
James Joyce