https://github.com/gogo/protobuf ya no se mantiene https://github.com/gogo/protobuf/issues/691 (actualmente)
Es nuestra dependencia, que es incompatible con la nueva versión golang/protobuf
, de la que dependen cada vez más paquetes, por lo que necesitamos reemplazar la versión golang/protobuf
, dependiendo de las versiones desactualizadas de nuestras dependencias directas y potencialmente incluso romper paquetes de esta manera
gogo/protobuf
dependencia
Resolver esto
¿Averiguar si aparecerá un nuevo mantenedor o un complemento diferente con paridad de funciones?
¿Usar solo protobuf de vainilla?
pruebas
sí
Complemento de validación que estamos usando soporte eliminado para GoGo
https://github.com/envoyproxy/protoc-gen-validate/pull/340
Creo que la mejor manera de avanzar es seguir el ecosistema y migrar lejos de gogo / protobuf. Con más y más de nuestras otras dependencias alejándose de gogo, creo que será cada vez más difícil seguir usándolo. Por supuesto que va a ser mucho trabajo migrar, así que si lo hacemos, debemos elaborar un buen plan.
@rvolosatovs probablemente sepa más sobre las opciones personalizadas que están configuradas en nuestro generador gogottn
, pero esto es lo que encontré para las opciones explícitas en nuestros archivos proto:
gogoproto.customname
, gogoproto.stdtime
y gogoproto.stdduration
, y goproto_enum_prefix
. Son relativamente fáciles de eliminar, ya que el compilador de Go se quejará inmediatamente de cualquier problema resultante.gogoproto.embed
significaría que ya no podemos acceder a los campos incrustados (el compilador Go nos ayudará a encontrarlos) y que los mensajes ya no satisfacen algunas interfaces (esto puede ser más difícil).gogoproto.nullable
será mucho más laboriosa, porque tendremos que empezar a usar los captadores y agregar cheques nulos. Es posible que el compilador de Go no detecte los problemas resultantes. Una posible solución sería convertir temporalmente esos campos en privados, luego reescribirlos en getters / setters y finalmente hacer que los campos sean públicos nuevamente.gogoproto.customtype
y enumeraciones que usan las opciones gogoproto.enum_stringer
. Para aquellos que a menudo hemos cambiado la forma en que se agrupan / desagrupan en JSON. Para los campos personalizados bytes
como EUI, DevAddr, etc., podríamos cambiar el tipo (en los mensajes proto) a string
(que es compatible con binarios). Con las enumeraciones, me temo que va a romper la API JSON, ya que ahora son aceptadas (por UnmarshalJSON) como cadenas e ints.Quizás este también sea un buen momento para empezar a pensar en nuestra API v4, porque me imagino que podríamos descubrir algunas sorpresas más (que rompen la API).
Creo que la mejor manera de avanzar sería probar primero https://github.com/alta/protopatch. Dependiendo del resultado:
api
)protopatch
si es un característica de bajo esfuerzo. Sin embargo, esto realmente depende de la opción, si estamos hablando de customtype
, que IMO definitivamente justifica contribuir, pero tal vez algo como stdtime
, no tanto.Mirando hacia el futuro, no creo que debamos usar directamente protos protobuf vanilla en componentes internamente en tiempo de ejecución (dado el conjunto de características provisto de protobuf hoy).
Solo tiene sentido usar protobuf para (des) serialización, es decir, para almacenamiento y en la capa de API. Sin embargo, internamente, el uso de protos Go generados con vainilla no tiene sentido para mí.
Entonces, por ejemplo, NS:
*ttnpb.EndDevice
(tipo Go generado vanilla) del registro, deserializado a partir de datos binarios almacenados*ttnpb.EndDevice
en T_device
, (NOTA: quizás eso podría ser solo una envoltura inicialmente o para siempre)T_device
internamente en NST_device
en *ttnpb.EndDevice
(NOTA: esta podría ser una tarea trivial y muy rápida si estamos usando un contenedor, ya que solo necesitamos modificar los campos cambiados y eso incluso podría realizarse en binarios datos directamente)*ttnpb.EndDevice
, serializar en datos binariosRefs también https://github.com/TheThingsNetwork/lorawan-stack/issues/342 (pobladores generados)
No estoy a favor de una alternativa (más pequeña) a gogo. Se siente como empujar la lata. Mantengamos las cosas tan simples como sea posible, especialmente cuando necesitamos decidir nuevamente cuál es la mejor manera de avanzar.
Estoy de acuerdo en que podemos considerar el uso de tipos intermedios en algunos lugares, en lugar de depender en todas partes de los protos generados. Básicamente, se trata de separar los objetos de transferencia de datos (DTO: protos, también para almacenamiento) de los objetos de acceso a datos (DAO: cómo los usamos). Si eso es principalmente lectura, también podemos declarar interfaces y ver hasta dónde llegamos con eso.
Dicho esto, no iría tan lejos como para cambiar todo el NS para usar T_device
, sino estructuras y / o interfaces específicas según sea necesario.
Pasemos esta discusión a noviembre
@rvolosatovs ¿cuál es su objeción en contra de pasar a vainilla con un marshaler JSON personalizado?
La enorme carga de migración y un montón de repetición si terminamos usando protos de vainilla directamente.
Sin embargo, realmente no me opongo a eso, solo creo que primero deberíamos intentar encontrar una alternativa simple no intrusiva y, si eso no es posible, luego recurrir a reelaborar todo esto.
Me temo que cualquier complemento en el que empecemos a confiar terminará en un estado sin mantenimiento en algún momento. En términos generales, estoy a favor de mantener las cosas lo más cerca posible de la vainilla. Si eso significa nil
verificar más a menudo de lo que nos gustaría, entonces que así sea. También puede funcionar a nuestro favor que sepamos que las cosas no están configuradas, en lugar de una estructura inicializada.
Me temo que refactorizar todo nuestro código base va a ser una molestia sin importar cómo lo hagamos. Nuestras estructuras proto (generadas por gogo) se utilizan en todas partes en este momento (API gRPC, API HTTP, eventos, errores, internamente, Redis DB, ...), por lo que cambiar a otra cosa (lo que sea que sea esa otra cosa) va a tocar prácticamente todo en nuestro código base, y la forma en que se ve ahora, todo al mismo tiempo.
El requisito estricto es que no rompamos la compatibilidad de nuestra API v3. Incluso si decidimos usar esta situación como el momento para comenzar a trabajar en una API v4 (al menos internamente), aún tendremos que seguir admitiendo esa API v3 para los usuarios existentes.
A largo plazo, creo que nos haríamos un gran favor al desvincular nuestras API externas (versionadas, estables dentro de las principales) de nuestra API interna (sin versionar, estable dentro de las secundarias) y nuestros documentos de base de datos (versionados, estables) . A continuación, podríamos escribir o generar funciones para convertir entre nuestras API internas y las demás.
Pero creo que hay algunos pasos que ya podemos tomar ahora:
Para mantener nuestra API JSON v3 compatible, creo que nuestro primer TODO es trabajar en la generación de marshalers y unmarshalers JSON que entiendan cómo ordenar / desarmar nuestros tipos personalizados. Creo que hacer esto es inteligente de todos modos porque no hay una promesa de estabilidad para la implementación de Go del formato JSON para búferes de protocolo , por lo que es mejor que tengamos el control sobre eso nosotros mismos. Hacer esto también podría permitirnos considerar las máscaras de campo al calcular las referencias en JSON. En el tiempo grpc-gateway
ejecución
Ya lo intenté aquí: https://github.com/TheThingsNetwork/lorawan-stack/commit/a41f62d98ae7ee719b576e6fcd2009a79cd38f4c
Esto hace que protobuf se queje del registro de tipos, por lo que es posible que necesitemos eliminar golang_proto.RegisterType
de nuestros antiguos protos para que esto funcione. Eliminar eso podría interrumpir la resolución de google.protobuf.Any
, pero solo los usamos en errores y eventos, por lo que probablemente podamos encontrar soluciones para esos casos específicos.
Esto es solo para el período de transición, pero para la solución a largo plazo, querríamos generar convertidores similares.
Ya probé eso con un servicio simple aquí: https://github.com/TheThingsNetwork/lorawan-stack/commit/cd7d75c8b42ad15eee1ac594ff6d0f2d5a75eb67 , pero para servicios más complicados definitivamente necesitaríamos esos convertidores.
Tenga en cuenta que esto solo cambia el servicio grpc en sí. El grpc-gateway todavía usa las cosas antiguas de gogo en el lado JSON y luego llama al servidor interno de gRPC, que luego ejecuta la nueva implementación.
Envió algunas actualizaciones de dependencia iniciales y soluciones alternativas de compatibilidad con versiones anteriores aquí: https://github.com/TheThingsNetwork/lorawan-stack/compare/issue/2798-codec
Cada vez más de nuestras dependencias se actualizan a protobuf 1.4 y la API V2, y cuanto más tiempo lo mantengamos abierto, más problemas tendremos al intentar actualizar nuestras dependencias.
Realmente deberíamos darle más prioridad a esto y tomar una decisión sobre lo que vamos a hacer al respecto.
Planifique una llamada para la próxima semana para que podamos hablar sin conexión.
Creo que deberíamos pasar por este proceso de dolor y concentrarnos en resolverlo en una semana o dos. Y para evitar que hagamos otras cosas, ya que esto va a causar muchos conflictos de lo contrario. Tener tantas manos como sea posible requiere saber exactamente qué vamos a hacer y en qué casos, dividir las tareas tanto como sea posible y tener la mirada puesta en el premio.
Próximos pasos:
unconvert
, gofumpt
y cualquier otra cosa que estemos haciendo además del protocoloprotoc-gen-gogottn
a protoc-gen-gofast
(o lo que sea más cercano a vainilla)(gogoproto.*)
opciones en nuestros archivos proto, para que se muestren igual que ahoragopls
y rf
puedan ayudar con esto.(gogoproto.*)
una por una y actualizamos el código que las usa. Quizás herramientas como gopls
y rf
puedan ayudar con esto.gogoproto.populate
y actualizando pruebas (https://github.com/TheThingsNetwork/lorawan-stack/issues/342)gogoproto.customname
y cambiar EUI -> Eui
etc.gogoproto.embed
. Necesitamos asegurarnos de que los mensajes aún implementen interfaces como ValidateContext(context.Context) error
y ExtractRequestFields(m map[string]interface{})
.gogoproto.nullable
y asegurándonos de que usamos Getters siempre que sea posible y, de lo contrario, no hagamos comprobaciones.@rvolosatovs intentemos dar esos primeros pasos para v3.11.3. Cuando haya terminado, vuelva a agregar los otros asignados y analicemos nuevamente.
Comentario más útil
Planifique una llamada para la próxima semana para que podamos hablar sin conexión.
Creo que deberíamos pasar por este proceso de dolor y concentrarnos en resolverlo en una semana o dos. Y para evitar que hagamos otras cosas, ya que esto va a causar muchos conflictos de lo contrario. Tener tantas manos como sea posible requiere saber exactamente qué vamos a hacer y en qué casos, dividir las tareas tanto como sea posible y tener la mirada puesta en el premio.