Lorawan-stack: Investigar el futuro del plugin gogo proto

Creado en 25 jun. 2020  ·  15Comentarios  ·  Fuente: TheThingsNetwork/lorawan-stack

Resumen

https://github.com/gogo/protobuf ya no se mantiene https://github.com/gogo/protobuf/issues/691 (actualmente)

¿Porqué necesitamos esto?

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

¿Qué hay ya ahí? ¿Qué ves ahora?

gogo/protobuf dependencia

¿Lo que falta? ¿Qué quieres ver?

Resolver esto

¿Cómo se propone implementar esto?

¿Averiguar si aparecerá un nuevo mantenedor o un complemento diferente con paridad de funciones?
¿Usar solo protobuf de vainilla?

¿Cómo propones probar esto?

pruebas

¿Puede hacer esto usted mismo y enviar una solicitud de extracción?

dependencies technical debt

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.

Todos 15 comentarios

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:

  • Podríamos comenzar eliminando las opciones 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.
  • Eliminar la opción 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).
  • La opción 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.
  • Lo que va a ser bastante problemático son los campos que usan 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:

  • Si todas nuestras necesidades están cubiertas -> migre y olvídese de esto (debería ser solo buscar y reemplazar en el directorio api )
  • Si solo se cubren algunas de nuestras necesidades y hay opciones personalizadas no cubiertas por el complemento -> deberíamos evaluar por opción y eliminar estas opciones personalizadas o, quizás, contribuir al 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:

  1. obtener *ttnpb.EndDevice (tipo Go generado vanilla) del registro, deserializado a partir de datos binarios almacenados
  2. convierta *ttnpb.EndDevice en T_device , (NOTA: quizás eso podría ser solo una envoltura inicialmente o para siempre)
  3. usar T_device internamente en NS
  4. convierta T_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)
  5. establecer *ttnpb.EndDevice , serializar en datos binarios

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:

JSON

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

Generar nuevos protos _junto a_ los antiguos

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.

Generar funciones para convertir entre protos antiguos y nuevos

Esto es solo para el período de transición, pero para la solución a largo plazo, querríamos generar convertidores similares.

Actualizar los servicios uno por uno

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:

  1. @rvolosatovs actualiza las herramientas para que estén lo más cerca posible de "vainilla":

    • Elimine cosas como unconvert , gofumpt y cualquier otra cosa que estemos haciendo además del protocolo

    • Cambie de protoc-gen-gogottn a protoc-gen-gofast (o lo que sea más cercano a vainilla)

    • Agregue explícitamente (gogoproto.*) opciones en nuestros archivos proto, para que se muestren igual que ahora

  2. Necesitamos refactorizar nuestro código para usar Getters en lugar de acceso directo al campo. Quizás herramientas como gopls y rf puedan ayudar con esto.
  3. Comenzamos a eliminar las opciones (gogoproto.*) una por una y actualizamos el código que las usa. Quizás herramientas como gopls y rf puedan ayudar con esto.

    • Eliminando gogoproto.populate y actualizando pruebas (https://github.com/TheThingsNetwork/lorawan-stack/issues/342)

    • Eliminar gogoproto.customname y cambiar EUI -> Eui etc.

    • Eliminando gogoproto.embed . Necesitamos asegurarnos de que los mensajes aún implementen interfaces como ValidateContext(context.Context) error y ExtractRequestFields(m map[string]interface{}) .

    • Eliminando 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.

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

htdvisser picture htdvisser  ·  4Comentarios

johanstokking picture johanstokking  ·  8Comentarios

johanstokking picture johanstokking  ·  8Comentarios

htdvisser picture htdvisser  ·  9Comentarios

kschiffer picture kschiffer  ·  6Comentarios