Proto-Serialisierung ist nicht kanonisch
Viele Leute möchten, dass ein serialisiertes Proto den Inhalt dieses Prto kannonisch darstellt. Anwendungsfälle umfassen
- Verwendung eines serialisierten Prto als Schlüssel in einer Hash-Tabelle
- Erstellung eines Fingerabdrucks oder einer Prüfsumme eines serialisierten Prtos
- Vergleich von serialisierten Payloads als Methode zur Überprüfung der Nachrichten-Gleichheit
Leider ist die Proto-Serialisierung nicht (und kann nicht) kanonisch. Es gibt einige bemerkenswerte Ausnahmen, wie z.B. MapReduce, aber im Allgemeinen sollten Sie die Proto-Serialisierung als instabil betrachten. Diese Seite erklärt, warum.
Determinismus ist nicht kanonisch
Deterministische Serialisierung ist nicht kanonisch. Der Serialisierer kann aus vielen Gründen unterschiedliche Ausgaben erzeugen, einschließlich, aber nicht beschränkt auf die folgenden Variationen
- Das Protobuf-Schema ändert sich auf irgendeine Weise.
- Die zu erstellende Anwendung ändert sich auf irgendeine Weise.
- Das Binärprogramm wird mit anderen Flags erstellt (z. B. opt vs. debug).
- Die Protobuf-Bibliothek wird aktualisiert.
Das bedeutet, dass Hashwerte von serialisierten Prtos fragil und nicht stabil über Zeit oder Raum sind.
Es gibt viele Gründe, warum sich die serialisierte Ausgabe ändern kann. Die obige Liste ist nicht erschöpfend. Einige davon sind inhärente Schwierigkeiten im Problembereich, die es ineffizient oder unmöglich machen würden, eine kanonische Serialisierung zu garantieren, selbst wenn wir es wollten. Andere sind Dinge, die wir absichtlich undefiniert lassen, um Optimierungsmöglichkeiten zu ermöglichen.
Inhärente Barrieren für stabile Serialisierung
Protobuf-Objekte bewahren unbekannte Felder, um Vorwärts- und Rückwärtskompatibilität zu gewährleisten. Die Handhabung unbekannter Felder ist ein primäres Hindernis für die kanonische Serialisierung.
Im Wire-Format verwenden Byte-Felder und verschachtelte Sub-Nachrichten denselben Wire-Typ. Diese Mehrdeutigkeit macht es unmöglich, Nachrichten, die im unbekannten Feld-Set gespeichert sind, korrekt zu kanonisieren. Da exakt derselbe Inhalt eines von beiden sein kann, ist es unmöglich zu wissen, ob er als Nachricht behandelt und rekursiv durchlaufen werden soll oder nicht.
Aus Effizienzgründen serialisieren Implementierungen typischerweise unbekannte Felder nach bekannten Feldern. Eine kanonische Serialisierung würde jedoch erfordern, unbekannte Felder gemäß der Feldnummer mit bekannten Feldern zu verschachteln. Dies würde erhebliche Effizienz- und Code-Größenkosten für alle Benutzer mit sich bringen, auch für diejenigen, die diese Funktion nicht benötigen.
Absichtlich undefinierte Dinge
Selbst wenn eine kanonische Serialisierung möglich wäre (d. h. wenn wir das Problem der unbekannten Felder lösen könnten), lassen wir die Serialisierungsreihenfolge absichtlich undefiniert, um mehr Optimierungsmöglichkeiten zu ermöglichen
- Wenn wir nachweisen können, dass ein Feld in einem Binärprogramm nie verwendet wird, können wir es vollständig aus dem Schema entfernen und als unbekanntes Feld verarbeiten. Dies spart erheblich Code-Größe und CPU-Zyklen.
- Es kann Möglichkeiten zur Optimierung geben, indem Vektoren desselben Feldes zusammen serialisiert werden, auch wenn dies die Feldnummernreihenfolge brechen würde.
Um Raum für solche Optimierungen zu lassen, wollen wir die Feldreihenfolge in einigen Konfigurationen absichtlich durcheinanderbringen, damit Anwendungen sich nicht unangemessen auf die Feldreihenfolge verlassen.