Lorawan-stack: Untersuche die Zukunft des Gogo-Proto-Plugins

Erstellt am 25. Juni 2020  ·  15Kommentare  ·  Quelle: TheThingsNetwork/lorawan-stack

Zusammenfassung

https://github.com/gogo/protobuf wird nicht mehr gepflegt https://github.com/gogo/protobuf/issues/691 (derzeit)

Warum brauchen wir das?

Es ist unsere Abhängigkeit, die mit der neuen golang/protobuf Version nicht kompatibel ist, von der immer mehr Pakete abhängen, daher müssen wir die golang/protobuf Version ersetzen, abhängig von veralteten Versionen unserer direkten Abhängigkeiten und möglicherweise sogar Pakete auf diese Weise brechen

Was ist schon da? Was siehst du jetzt?

gogo/protobuf Abhängigkeit

Was fehlt? Was willst du sehen?

Finde es heraus

Wie wollen Sie dies umsetzen?

Herausfinden, ob ein neuer Betreuer erscheinen wird oder ein anderes Plugin mit Funktionsparität?
Nur Vanille-Protobuf verwenden?

Wie schlagen Sie vor, dies zu testen?

Tests

Können Sie dies selbst tun und einen Pull Request senden?

ja

dependencies technical debt

Hilfreichster Kommentar

Bitte planen Sie für nächste Woche einen Anruf ein, damit wir offline besprechen können.

Ich denke, wir sollten diesen Schmerzprozess durchlaufen und uns darauf konzentrieren, dies in ein oder zwei Wochen zu lösen. Und um zu vermeiden, dass wir andere Dinge tun, da dies sonst viele Konflikte verursachen wird. Möglichst viele Hände zu haben erfordert, genau zu wissen, was wir in welchen Fällen tun werden, Aufgaben möglichst aufzuteilen und den Gewinn im Auge zu behalten.

Alle 15 Kommentare

Validierungs-Plugin, wir verwenden die eingestellte Unterstützung für GoGo
https://github.com/envoyproxy/protoc-gen-validate/pull/340

Ich denke, der beste Weg nach vorne besteht darin, dem Ökosystem zu folgen und von Gogo/Protobuf wegzugehen. Da sich immer mehr unserer anderen Abhängigkeiten von Gogo entfernen, wird es meiner Meinung nach immer schwieriger, es weiterhin zu verwenden. Natürlich wird die Migration eine Menge Arbeit sein, also müssen wir uns einen guten Plan ausdenken, wenn wir dies tun.

@rvolosatovs weiß wahrscheinlich mehr über die benutzerdefinierten Optionen, die in unserem gogottn Generator festgelegt sind, aber Folgendes habe ich für die expliziten Optionen in unseren Proto-Dateien gefunden:

  • Wir könnten damit beginnen, die Optionen gogoproto.customname , gogoproto.stdtime und gogoproto.stdduration und goproto_enum_prefix entfernen. Diese sind relativ einfach zu entfernen, da der Go-Compiler alle daraus resultierenden Probleme sofort beschwert.
  • Das Entfernen der Option gogoproto.embed würde bedeuten, dass wir nicht mehr auf eingebettete Felder zugreifen können (der Go-Compiler hilft uns dabei, diese zu finden) und dass Nachrichten einige Schnittstellen nicht mehr erfüllen (dies kann schwieriger sein).
  • Die Option gogoproto.nullable wird viel mehr Arbeit bedeuten, da wir damit beginnen müssen, die Getter zu verwenden und Nullprüfungen hinzuzufügen. Daraus resultierende Probleme werden möglicherweise nicht vom Go-Compiler abgefangen. Eine mögliche Problemumgehung wäre, diese Felder vorübergehend privat zu machen, dann in Getter/Setter umzuschreiben und die Felder schließlich wieder öffentlich zu machen.
  • Was ziemlich problematisch sein wird, sind die Felder, die gogoproto.customtype und Aufzählungen, die gogoproto.enum_stringer Optionen verwenden. Für diese haben wir oft die Art und Weise geändert, wie sie in JSON gemarshallt/unmarshallt werden. Für die benutzerdefinierten bytes Felder wie EUI, DevAddr usw. könnten wir den Typ (in den Proto-Nachrichten) in string ändern (was binär kompatibel ist). Ich fürchte, mit den Enumerationen wird die JSON-API beschädigt, da diese jetzt (von UnmarshalJSON) sowohl als Strings als auch als Ints akzeptiert werden.

Vielleicht ist dies auch ein guter Zeitpunkt, um über unsere v4-API nachzudenken, denn ich kann mir vorstellen, dass wir noch einige (API-brechende) Überraschungen entdecken werden.

Ich denke, der beste Weg wäre, zuerst https://github.com/alta/protopatch auszuprobieren

  • Wenn alle unsere Bedürfnisse abgedeckt sind -> migrieren und vergessen Sie dies (sollte nur im api Verzeichnis suchen und ersetzen)
  • Wenn nur einige unserer Bedürfnisse abgedeckt werden und es benutzerdefinierte Optionen gibt, die vom Plugin nicht abgedeckt werden -> sollten wir auf Basis der einzelnen Optionen auswerten und diese benutzerdefinierten Optionen entweder entfernen oder vielleicht zum protopatch beitragen, wenn es ein Low-Effort-Funktion. Dies hängt jedoch wirklich von der Option ab – wenn wir über customtype sprechen – die IMO definitiv einen Beitrag rechtfertigt, aber vielleicht etwas wie stdtime – nicht so sehr.

Mit Blick auf die Zukunft denke ich nicht, dass wir Vanilla-Protobuf-Protos direkt in Komponenten zur Laufzeit intern verwenden sollten (angesichts des heute bereitgestellten Feature-Sets von protobuf).
Es ist nur sinnvoll, protobuf für die (De-)Serialisierung, also für die Speicherung und auf API-Ebene zu verwenden. Intern macht es für mich jedoch keinen Sinn, mit Plain Vanilla generierte Go-Protos zu verwenden.
Also zum Beispiel NS:

  1. Hole *ttnpb.EndDevice (von Vanilla generierter Go-Typ) aus der Registry, deserialisiert aus gespeicherten Binärdaten
  2. Konvertiere *ttnpb.EndDevice in T_device , (HINWEIS: vielleicht könnte das anfangs oder für immer nur ein Wrapper sein)
  3. T_device intern in NS verwenden
  4. Konvertieren von T_device in *ttnpb.EndDevice (HINWEIS: Dies könnte eine triviale, sehr schnelle Aufgabe sein, wenn wir einen Wrapper verwenden, da wir nur geänderte Felder ändern müssen und dies sogar auf Binärdateien möglich ist Daten direkt)
  5. setze *ttnpb.EndDevice , serialisiere in binäre Daten

Ich bin nicht für eine (kleinere) Alternative zu Gogo. Es fühlt sich an, als würde man die Dose schieben. Lassen Sie uns die Dinge so Vanille wie möglich halten, insbesondere wenn wir erneut entscheiden müssen, was der beste Weg ist.

Ich stimme zu, dass wir an einigen Stellen die Verwendung von Zwischentypen in Betracht ziehen können, anstatt sich überall auf die generierten Protos zu verlassen. Es trennt im Wesentlichen Datenübertragungsobjekte (DTOs: Protos, auch für die Speicherung) von Datenzugriffsobjekten (DAOs: wie wir sie verwenden). Wenn das hauptsächlich Lesen ist, können wir auch Schnittstellen deklarieren und sehen, wie weit wir damit kommen.

Das heißt, ich würde nicht so weit gehen, den gesamten NS zu ändern, um T_device , sondern nach Bedarf spezifische Strukturen und/oder Schnittstellen.

Verschieben wir diese Diskussion in den November

@rvolosatovs was sind Ihre Einwände gegen den Wechsel zu Vanilla mit einem benutzerdefinierten JSON-Marshaler?

Die enorme Migrationslast und jede Menge Boilerplate, wenn wir am Ende nur Vanille-Protos direkt verwenden.
Ich habe dagegen nicht wirklich Einwände, ich denke nur, wir sollten zuerst versuchen, eine einfache, nicht aufdringliche Alternative zu finden, und wenn das nicht möglich ist, dann zu einer Überarbeitung des ganzen Dings greifen.

Ich befürchte, dass jedes Plugin, auf das wir uns verlassen, irgendwann in einem nicht gewarteten Zustand endet. Generell bin ich dafür, die Dinge so nah wie möglich an Vanille zu halten. Wenn das bedeutet, dass nil öfter überprüft wird, als uns lieb ist, dann soll es so sein. Es kann auch zu unseren Gunsten wirken, dass wir wissen, dass Dinge nicht gesetzt sind, anstatt eine initialisierte Struktur.

Ich befürchte, dass das Refactoring unserer gesamten Codebasis mühsam sein wird, egal wie wir es machen. Unsere (gogo-generierten) Proto-Strukturen werden derzeit überall verwendet (gRPC-API, HTTP-API, Ereignisse, Fehler, intern, Redis DB, ...), also wird der Wechsel zu etwas anderem (was auch immer das andere ist) berühren so ziemlich alles in unserer Codebasis und wie es jetzt aussieht, gleichzeitig.

Die harte Anforderung ist, dass wir die Kompatibilität unserer v3-API nicht unterbrechen. Selbst wenn wir diese Situation als Moment nutzen, um mit der Arbeit an einer v4-API (zumindest intern) zu beginnen, müssen wir diese v3-API für bestehende Benutzer weiterhin unterstützen.

Langfristig denke ich, dass wir uns selbst einen großen Gefallen tun würden, wenn wir unsere (versionierten, stabilen-innerhalb-Major) externen APIs von unserer internen (unversionierten, stabilen-innerhalb-kleineren) API und unseren (versionierten, stabilen) Datenbankdokumenten entkoppeln würden . Wir könnten dann Funktionen schreiben oder generieren, um zwischen unseren internen APIs und den anderen zu konvertieren.

Aber ich denke, es gibt einige Schritte, die wir jetzt schon machen können:

JSON

Um unsere v3-JSON-API kompatibel zu halten, besteht unsere erste TODO meiner Meinung nach darin, an der Generierung von JSON-Marshalern und Unmarshalern zu arbeiten, die verstehen, wie unsere benutzerdefinierten Typen gemarshallt/unmarshallt werden. Ich denke, dies ist sowieso klug, weil es kein Stabilitätsversprechen für Gos Implementierung des JSON-Formats für Protokollpuffer gibt , also haben wir das besser selbst in der Hand. Auf diese Weise können wir auch Feldmasken beim Marshallen an JSON berücksichtigen. In der grpc-gateway Laufzeit können wir Codecs registrieren, also können wir einfach einen Codec schreiben, der unsere eigenen (generierten) (un)marshaler anstelle von {gogo,golang}/protobufs jsonpb aufruft.

Generiere neue Protos _neben_ den alten

Das habe ich hier schon probiert: https://github.com/TheThingsNetwork/lorawan-stack/commit/a41f62d98ae7ee719b576e6fcd2009a79cd38f4c

Dies führt dazu, dass sich Protobuf über die Typenregistrierung beschwert, daher müssen wir möglicherweise golang_proto.RegisterType aus unseren alten Protos entfernen, damit dies funktioniert. Das Entfernen könnte möglicherweise die Auflösung von google.protobuf.Any , aber wir verwenden diese nur in Fehlern und Ereignissen, sodass wir wahrscheinlich Problemumgehungen für diese speziellen Fälle finden können.

Generieren Sie Funktionen zum Konvertieren zwischen alten und neuen Prototypen

Dies ist nur für die Übergangszeit, aber für die langfristige Lösung möchten wir ähnliche Konverter generieren.

Aktualisieren Sie die Dienste einzeln

Ich habe das bereits mit einem einfachen Dienst hier versucht: https://github.com/TheThingsNetwork/lorawan-stack/commit/cd7d75c8b42ad15eee1ac594ff6d0f2d5a75eb67 , aber für kompliziertere Dienste würden wir diese Konverter definitiv brauchen.

Beachten Sie, dass dies nur den grpc-Dienst selbst ändert. Das grpc-Gateway verwendet auf der JSON-Seite immer noch das alte Gogo-Stuff und ruft dann den internen gRPC-Server auf, der dann die neue Implementierung ausführt.

Einige anfängliche Abhängigkeitsupdates und Abwärtskompatibilitäts-Workarounds wurden hier veröffentlicht: https://github.com/TheThingsNetwork/lorawan-stack/compare/issue/2798-codec

Immer mehr unserer Abhängigkeiten werden auf Protobuf 1.4 und die V2-API aktualisiert, und je länger wir dies offen halten, desto mehr Probleme werden wir haben, wenn wir versuchen, unsere Abhängigkeiten zu aktualisieren.

Wir sollten dem wirklich etwas mehr Priorität einräumen und eine Entscheidung treffen, was wir in dieser Hinsicht tun werden.

Bitte planen Sie für nächste Woche einen Anruf ein, damit wir offline besprechen können.

Ich denke, wir sollten diesen Schmerzprozess durchlaufen und uns darauf konzentrieren, dies in ein oder zwei Wochen zu lösen. Und um zu vermeiden, dass wir andere Dinge tun, da dies sonst viele Konflikte verursachen wird. Möglichst viele Hände zu haben erfordert, genau zu wissen, was wir in welchen Fällen tun werden, Aufgaben möglichst aufzuteilen und den Gewinn im Auge zu behalten.

Nächste Schritte:

  1. @rvolosatovs aktualisiert das Tooling so nah wie möglich an "Vanille":

    • Entferne Dinge wie unconvert , gofumpt und was wir sonst noch zusätzlich zum Protokoll machen

    • Wechseln Sie von protoc-gen-gogottn zu protoc-gen-gofast (oder was auch immer der Vanille am nächsten kommt)

    • Fügen Sie unseren Proto-Dateien explizit (gogoproto.*) Optionen hinzu, damit sie genauso gerendert werden wie jetzt

  2. Wir müssen unseren Code umgestalten, um Getters anstelle des direkten Feldzugriffs zu verwenden. Vielleicht können Tools wie gopls und rf dabei helfen.
  3. Wir beginnen mit dem Entfernen der (gogoproto.*) Optionen nacheinander und aktualisieren den Code, der sie verwendet. Vielleicht können Tools wie gopls und rf dabei helfen.

    • Entfernen von gogoproto.populate und Aktualisieren von Tests (https://github.com/TheThingsNetwork/lorawan-stack/issues/342)

    • Entfernen von gogoproto.customname und Ändern von EUI -> Eui usw.

    • Entfernen von gogoproto.embed . Wir müssen sicherstellen, dass Nachrichten weiterhin Schnittstellen wie ValidateContext(context.Context) error und ExtractRequestFields(m map[string]interface{}) implementieren.

    • Entfernen Sie gogoproto.nullable und stellen Sie sicher, dass wir Getter verwenden, wo dies möglich ist, und ansonsten keine Überprüfungen durchführen.

    • ...

@rvolosatovs versuchen wir, diese ersten Schritte für v3.11.3 zu machen. Wenn das erledigt ist, fügen Sie bitte die anderen Bevollmächtigten wieder hinzu und lassen Sie uns erneut diskutieren.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen