web-dev-qa-db-fra.com

Comment le tampon de protocole gère-t-il la gestion des versions?

Comment les tampons de protocole gèrent-ils la gestion des versions?

Par exemple, quand dois-je changer une définition de type au fil du temps? Comme ajouter et supprimer des champs.

35
CodingHero

Google a conçu le protobuf pour être assez tolérant avec le contrôle de version:

  • les données inattendues sont soit stockées en tant qu '"extensions" (ce qui les rend sécurisées pour un aller-retour), soit supprimées silencieusement, selon l'implémentation
  • les nouveaux champs sont généralement ajoutés comme "facultatifs", ce qui signifie que les anciennes données peuvent être chargées avec succès

toutefois:

  • ne pas renuméroter champs - cela casserait les données existantes
  • vous ne devriez pas normalement changer la façon dont un champ donné est stocké (c'est-à-dire d'un int fixe avec 32 bits à un "varint")

D'une manière générale, cependant - cela fonctionnera fonctionnera simplement, et vous n'avez pas à vous soucier du versioning.

25
Marc Gravell

Je sais que c'est une vieille question, mais je l'ai rencontrée récemment. La façon dont je l'ai contourné est d'utiliser les façades et les décisions d'exécution pour sérialiser. De cette façon, je peux déprécier/mettre à niveau un champ dans un nouveau type, avec des messages anciens et nouveaux qui le gèrent avec élégance.

J'utilise protobuf.net de Marc Gravell (v2.3.5) et C #, mais la théorie des façades fonctionnerait pour n'importe quel langage et la mise en œuvre originale de protobuf de Google.

Mon ancienne classe avait un horodatage de DateTime que je voulais changer pour inclure le "Kind" (un anachronisme .NET). L'ajout de cela signifiait effectivement qu'il était sérialisé à 9 octets au lieu de 8, ce qui serait un changement de sérialisation!

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

Un principe fondamental de protobuf est de ne JAMAIS changer les proto ids! Je voulais lire les anciens binaires sérialisés, ce qui signifiait que "3" était là pour rester.

Alors,

J'ai renommé l'ancienne propriété et l'ai rendue privée (oui, elle peut toujours se désérialiser par la magie de la réflexion), mais mon API ne la montre plus utilisable!

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

J'ai créé une nouvelle propriété Timestamp, avec un nouvel ID de proto, et inclus le DateTime.Kind

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

J'ai ajouté une méthode "AfterDeserialization" pour mettre à jour notre nouvelle heure, dans le cas d'anciens messages

    [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...
        }
    }

Maintenant, j'ai les anciens et les nouveaux messages sérialisant/désérialisant correctement, et mon horodatage inclut maintenant DateTime.Kind! Rien de cassé.

Cependant, cela signifie que les DEUX champs figureront dans tous les nouveaux messages à l'avenir. Donc, la touche finale est d'utiliser une décision de sérialisation au moment de l'exécution pour exclure l'ancien horodatage (notez que cela ne fonctionnera pas s'il utilisait l'attribut requis de protobuf !!!)

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

Et c'est tout. J'ai un test unitaire sympa qui le fait de bout en bout si quelqu'un le veut ...

Je sais que ma méthode repose sur la magie .NET, mais je pense que le concept pourrait être traduit dans d'autres langues ....

7
James Joyce