it-swarm-pt.tech

Como o buffer de protocolo lida com o controle de versão?

Como os buffers de protocolo lidam com a versão do tipo?

Por exemplo, quando preciso alterar uma definição de tipo ao longo do tempo? Como adicionar e remover campos.

35
CodingHero

O Google projetou o protobuf para perdoar bastante o versionamento:

  • dados inesperados são armazenados como "extensões" (tornando-os seguros de ida e volta) ou eliminados silenciosamente, dependendo da implementação
  • novos campos são geralmente adicionados como "opcionais", o que significa que dados antigos podem ser carregados com êxito

contudo:

  • não renumerar campos - isso quebraria os dados existentes
  • você normalmente não deve alterar a maneira como um determinado campo é armazenado (ou seja, de um int de 32 bits fixo para um "varint")

De um modo geral, porém - ele apenas funciona, e você não precisa se preocupar muito com o controle de versão.

25
Marc Gravell

Sei que essa é uma pergunta antiga, mas me deparei com isso recentemente. A maneira como eu consegui contornar isso é usar fachadas e decisões em tempo de execução para serializar. Dessa forma, posso descontinuar/atualizar um campo para um novo tipo, com mensagens antigas e novas manipulando-o normalmente.

Estou usando o protobuf.net de Marc Gravell (v2.3.5) e o C #, mas a teoria das fachadas funcionaria em qualquer idioma e na implementação original do protobuf do Google.

Minha classe antiga tinha um carimbo de data/hora de DateTime que eu queria alterar para incluir o "Kind" (um anacronismo .NET). Adicionando isso efetivamente significava que seria serializado em 9 bytes em vez de 8, o que seria uma alteração de serialização de última hora!

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

Um dos fundamentos do protobuf NUNCA é alterar os IDs dos proto! Eu queria ler binários serializados antigos, o que significava que "3" estava aqui para ficar.

Assim,

Renomeei a propriedade antiga e a tornei privada (sim, ela ainda pode desserializar por meio da magia de reflexão), mas minha API não a mostra mais utilizável!

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

Criei uma nova propriedade Timestamp, com um novo ID de proto e incluí o DateTime.Kind

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

Eu adicionei um método "AfterDeserialization" para atualizar nosso novo horário, no caso de mensagens antigas

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

Agora, tenho as novas e antigas mensagens serializando/desserializando corretamente, e meu carimbo de data/hora agora inclui DateTime.Kind! Nada quebrado.

No entanto, isso significa que AMBOS os campos estarão em todas as novas mensagens daqui para frente. Portanto, o toque final é usar uma decisão de serialização em tempo de execução para excluir o carimbo de data/hora antigo (observe que isso não funcionará se estiver usando o atributo necessário do protobuf !!!)

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

E é isso. Eu tenho um teste de unidade de Nice que faz de ponta a ponta, se alguém quiser ...

Eu sei que meu método depende da magia do .NET, mas acho que o conceito pode ser traduzido para outros idiomas ....

7
James Joyce