https://github.com/gogo/protobuf não é mais mantido https://github.com/gogo/protobuf/issues/691 (atualmente)
É nossa dependência, que é incompatível com a nova versão golang/protobuf
, da qual mais e mais pacotes dependem, portanto, precisamos substituir a versão golang/protobuf
, dependendo das versões desatualizadas de nossas dependências diretas e potencialmente até quebrando pacotes desta forma
Dependência de gogo/protobuf
Descobrir isso
Descobrir se um novo mantenedor aparecerá ou um plugin diferente com paridade de recursos?
Usar apenas protobuf de baunilha?
testes
sim
Plug-in de validação que estamos usando para compatibilidade com GoGo
https://github.com/envoyproxy/protoc-gen-validate/pull/340
Acho que o melhor caminho a seguir é seguir o ecossistema e migrar para longe do gogo / protobuf. Com cada vez mais nossas outras dependências se afastando do gogo, acho que será cada vez mais difícil continuar a usá-lo. É claro que vai dar muito trabalho migrar, então, se fizermos isso, precisaremos apresentar um bom plano.
@rvolosatovs provavelmente sabe mais sobre as opções personalizadas definidas em nosso gerador gogottn
, mas aqui está o que encontrei para as opções explícitas em nossos arquivos proto:
gogoproto.customname
, gogoproto.stdtime
e gogoproto.stdduration
e goproto_enum_prefix
. Esses são relativamente fáceis de remover, uma vez que o compilador Go reclamará imediatamente sobre quaisquer problemas resultantes.gogoproto.embed
significaria que não podemos mais acessar os campos incorporados (o compilador Go nos ajudará a encontrá-los) e que as mensagens não atenderão mais a algumas interfaces (isso pode ser mais difícil).gogoproto.nullable
será muito mais trabalhosa, porque teremos que começar a usar os getters e adicionar nil-checks. Os problemas resultantes podem não ser detectados pelo compilador Go. A solução possível seria tornar temporariamente esses campos privados e, em seguida, reescrever para getters / setters e, finalmente, tornar os campos públicos novamente.gogoproto.customtype
e enums que usam gogoproto.enum_stringer
options. Para aqueles, muitas vezes mudamos a maneira como eles são empacotados / desempacotados para JSON. Para os campos bytes
, como EUI, DevAddr etc., poderíamos alterar o tipo (nas mensagens proto) para string
(que é compatível com o binário). Com os enums, temo que vá quebrar a API JSON, já que eles agora são aceitos (por UnmarshalJSON) como strings e ints.Talvez este também seja um bom momento para começar a pensar em nossa API v4, porque posso imaginar que possamos descobrir mais algumas surpresas (quebra de API).
Acho que a melhor maneira de avançar seria primeiro experimentar https://github.com/alta/protopatch. Dependendo do resultado:
api
)protopatch
se for um recurso de baixo esforço. No entanto, isso realmente depende da opção - se estamos falando de customtype
- que a IMO definitivamente justifica a contribuição, mas talvez algo como stdtime
- nem tanto.Olhando para o futuro, não acho que devamos usar diretamente protobuf protos vanilla em componentes internamente em tempo de execução (dado o conjunto de recursos fornecido de protobuf hoje).
Só faz sentido usar protobuf para (des) serialização, ou seja, para armazenamento e na camada API. Internamente, no entanto, usar protos Go gerados por plain vanilla não faz sentido para mim.
Então, por exemplo, NS:
*ttnpb.EndDevice
(tipo Go gerado por vanilla) do registro, desserializado dos dados binários armazenados*ttnpb.EndDevice
em T_device
, (NOTA: talvez isso possa ser apenas um invólucro inicialmente ou para sempre)T_device
internamente no NST_device
em *ttnpb.EndDevice
(NOTA: esta pode ser uma tarefa trivial e muito rápida se estivermos usando um wrapper, uma vez que só precisamos modificar os campos alterados e isso pode até mesmo ser executado em binários dados diretamente)*ttnpb.EndDevice
, serializar em dados bináriosRefs também https://github.com/TheThingsNetwork/lorawan-stack/issues/342 (populadores gerados)
Não sou a favor de uma alternativa (menor) ao gogo. É como empurrar a lata. Vamos manter as coisas o mais simples possível, especialmente quando precisamos decidir novamente qual é o melhor caminho a seguir.
Eu concordo que podemos considerar o uso de tipos intermediários em alguns lugares, em vez de depender de todos os protos gerados. É basicamente separar objetos de transferência de dados (DTOs: protos, também para armazenamento) de objetos de acesso a dados (DAOs: como os usamos). Se for principalmente leitura, também podemos declarar interfaces e ver o quão longe chegamos com isso.
Dito isso, eu não iria tão longe a ponto de mudar todo o NS para usar T_device
, mas sim estruturas e / ou interfaces específicas conforme necessário.
Vamos mover essa discussão para novembro
@rvolosatovs qual é sua objeção contra a mudança para o vanilla com um empacotador JSON personalizado?
A enorme carga de migração e cargas de clichê se acabarmos usando apenas protos vanilla diretamente.
Eu realmente não me oponho a isso, só acho que devemos tentar encontrar uma alternativa simples e não intrusiva primeiro e, se isso não for possível, recorrer a refazer tudo isso.
Receio que qualquer plugin com o qual começamos a contar acabará sem manutenção em algum ponto. De um modo geral, sou a favor de manter as coisas o mais próximo possível da baunilha. Se isso significa que nil
verifica com mais frequência do que gostaríamos, então que seja. Também pode funcionar a nosso favor que saibamos que as coisas não estão definidas, em vez de uma estrutura inicializada.
Temo que refatorar toda a nossa base de código será uma dor, não importa como o façamos. Nossas protoestruturas (geradas por gogo) são usadas em todos os lugares agora (gRPC API, HTTP API, eventos, erros, internamente, Redis DB, ...), portanto, mudar para outra coisa (seja lá o que for) afetará praticamente tudo em nossa base de código e a forma como está agora, tudo ao mesmo tempo.
O principal requisito é que não quebremos a compatibilidade de nossa API v3. Mesmo se decidirmos usar essa situação como o momento para começar a trabalhar em uma API v4 (pelo menos internamente), ainda teremos que manter o suporte a essa API v3 para os usuários existentes.
A longo prazo, acho que faríamos a nós mesmos um grande favor desacoplando nossas APIs externas (com versão, estável dentro do principal) de nossa API interna (sem versão, estável dentro do menor) e nossos documentos de banco de dados (com versão, estável) . Poderíamos então escrever ou gerar funções para converter entre nossas APIs internas e as outras.
Mas acho que existem alguns passos que já podemos dar agora:
A fim de manter nossa API JSON v3 compatível, acho que nosso primeiro TODO é trabalhar na geração de marshalers e unmarshalers JSON que entendam como empacotar / desempacotar nossos tipos personalizados. Acho que fazer isso é inteligente de qualquer maneira, porque não há promessa de estabilidade para a implementação do formato JSON do Go para buffers de protocolo , então é melhor termos controle sobre isso nós mesmos. Isso também pode nos permitir considerar as máscaras de campo ao fazer o empacotamento para JSON. No tempo de execução grpc-gateway
, podemos registrar codecs, portanto, podemos apenas escrever um codec que chame nossos próprios (gerados) (des) marshalers em vez de jsonpb de {gogo, golang} / protobuf.
Já tentei isso aqui: https://github.com/TheThingsNetwork/lorawan-stack/commit/a41f62d98ae7ee719b576e6fcd2009a79cd38f4c
Isso faz com que o protobuf reclame sobre o registro de tipos, então podemos precisar remover golang_proto.RegisterType
de nossos protos antigos para fazer isso funcionar. A remoção disso poderia potencialmente interromper a resolução de google.protobuf.Any
, mas só os usamos em erros e eventos, portanto, provavelmente podemos encontrar soluções alternativas para esses casos específicos.
Isso é apenas para o período de transição, mas para a solução de longo prazo, gostaríamos de gerar conversores semelhantes.
Já tentei isso com um serviço simples aqui: https://github.com/TheThingsNetwork/lorawan-stack/commit/cd7d75c8b42ad15eee1ac594ff6d0f2d5a75eb67 , mas para serviços mais complicados, definitivamente precisaríamos desses conversores.
Observe que isso apenas altera o próprio serviço grpc. O grpc-gateway ainda usa o antigo gogo no lado JSON e, em seguida, chama o servidor gRPC interno, que executa a nova implementação.
Enviou algumas atualizações de dependência inicial e soluções de compatibilidade com versões anteriores aqui: https://github.com/TheThingsNetwork/lorawan-stack/compare/issue/2798-codec
Mais e mais de nossas dependências estão atualizando para protobuf 1.4 e a API V2, e quanto mais mantivermos isso aberto, mais problemas teremos ao tentar atualizar nossas dependências.
Devíamos realmente dar mais prioridade a isso e tomar uma decisão sobre o que faremos a respeito de tudo isso.
Planeje uma ligação para a próxima semana para que possamos discutir off-line.
Acho que devemos passar por esse processo doloroso e nos concentrar em resolver isso em uma ou duas semanas. E para evitar que façamos outras coisas, pois de outra forma, isso vai causar muitos conflitos. Ter o maior número de mãos possível exige saber exatamente o que vamos fazer e em que casos, dividindo as tarefas o máximo possível e de olho no prêmio.
Próximos passos:
unconvert
, gofumpt
e tudo o mais que estivermos fazendo em cima do protocoloprotoc-gen-gogottn
para protoc-gen-gofast
(ou o que quer que seja mais próximo ao baunilha)(gogoproto.*)
options em nossos arquivos proto, para que sejam processados da mesma forma que agoragopls
e rf
possam ajudar com isso.(gogoproto.*)
options uma por uma e atualizamos o código que as utiliza. Talvez ferramentas como gopls
e rf
possam ajudar com isso.gogoproto.populate
e atualizando os testes (https://github.com/TheThingsNetwork/lorawan-stack/issues/342)gogoproto.customname
e alterando EUI -> Eui
etc.gogoproto.embed
. Precisamos ter certeza de que as mensagens ainda implementam interfaces como ValidateContext(context.Context) error
e ExtractRequestFields(m map[string]interface{})
.gogoproto.nullable
e certificando-se de usar Getters sempre que possível e não fazer nenhuma verificação caso contrário.@rvolosatovs vamos tentar dar os primeiros passos para a v3.11.3. Quando terminar, adicione novamente os outros designados e vamos discutir novamente.
Comentários muito úteis
Planeje uma ligação para a próxima semana para que possamos discutir off-line.
Acho que devemos passar por esse processo doloroso e nos concentrar em resolver isso em uma ou duas semanas. E para evitar que façamos outras coisas, pois de outra forma, isso vai causar muitos conflitos. Ter o maior número de mãos possível exige saber exatamente o que vamos fazer e em que casos, dividindo as tarefas o máximo possível e de olho no prêmio.