結論
- .proto定義に必須フィールドを追加すると、新しい定義でビルドされたバイナリは、古いデータに必須フィールドが存在しないため、古い定義を使用してシリアル化されたデータを解析できなくなるから
- proto定義の変更でprotoを共有しているアプリケーションの互換性が崩れる
詳しくは後述します。
概要
Protocol BufferのベストプラクティスにDon't Add a Required Fireldという項があり実際にProto3では必須フィールドが使えなくなりました。
なぜこのようなことになったのか調べてみました。
調べてみると、次のStackOverflowの記事が見付かりました。
回答を翻訳しました。
必須フィールドの有用性については、多くの議論や論争が巻き起こってきました。双方に大きな陣営が存在しています。一方の陣営は、値が存在することを保証することが好きで、その制限を受け入れることになりますが、もう一方の陣営は、必須のフィールドは安全に追加または削除できないため、危険または役に立たないと感じています。
必須フィールドが控えめに使用されるべき理由について、もう少し説明しましょう。既にProtoを使用している場合、古いアプリケーションはそのフィールドを提供しないため、必須フィールドを追加することはできません。一般的に、アプリケーションは失敗をうまく処理できません。すべての古いアプリケーションを最初にアップグレードすることができますが、間違いを犯しやすいですし、Protoをどのデータストアに保存しているか(メモリーキャッシュなど、短命なものでも)に関係なく、役に立ちません。必須フィールドを削除する場合も同様の状況が発生します。
多くの必須フィールドは、それが必要だと思われていた...が、必要ではなくなったということがあります。例えば、Getメソッドのidフィールドがあるとします。それは明らかに必要です。ただし、後でidをintからstring、またはint32からint64に変更する必要があるかもしれません。これにより、新しいmuchBetterIdフィールドが追加され、古いidフィールドが指定される必要がありますが、最終的には完全に無視されます。
これらの2つの問題が組み合わさると、有益な必須フィールドの数は限られ、陣営はそれでも価値があるかどうかについて議論します。必須フィールドの反対者は、必ずしもそのアイデアに反対していたわけではありませんが、現在の形態に反対していました。いくつかの人々は、より表現豊かなバリデーションライブラリを開発することを提案し、name.length>10のようなより高度なバリデーションをチェックすることができ、さらに良い失敗モデルを持つようにしました。
Proto3は全体的にシンプルさを重視しており、必須フィールドの削除はシンプルです。ただし、proto3の場合、プリミティブのフィールドの存在を削除し、デフォルト値の上書きを削除するなど、他の機能と組み合わせることで、必須フィールドの削除が合理的であると考えられます。
私はprotobufの開発者ではありませんし、この問題についての権威的な知識を持っているわけではありませんが、この説明が役立つことを願っています。
さらに調べると、実際に必須フィールドが削除されたGitHubのissuesが見付かります。why messge type remove 'required,optional'? · Issue #2497 · protocolbuffers/protobuf
抜粋して翻訳します。
proto3では、requiredフィールドが一般的に有害であり、protobufの互換性セマンティクスに違反するため、必須フィールドを廃止しました。protobufを使用する全体的なアイデアは、プロトコル定義からフィールドを追加/削除でき、新しい/古いバイナリと完全に前方/後方互換性があることです。しかし、必須フィールドはこれを破壊します。.proto定義に必須フィールドを安全に追加することはできず、既存の必須フィールドを安全に削除することもできません。これらの両方のアクションはワイヤ互換性を破壊します。たとえば、.proto定義に必須フィールドを追加すると、新しい定義でビルドされたバイナリは、古いデータに必須フィールドが存在しないため、古い定義を使用してシリアル化されたデータを解析できません。.proto定義がシステムの多くの異なるコンポーネントで広く共有される複雑なシステムでは、必須フィールドを追加/削除することで、システムの複数の部分を簡単にダウンさせることができます。これにより、生産上の問題が複数回発生し、Google内のどこでも誰でも必須フィールドを追加/削除することはほぼ禁止されています。そのため、proto3では完全に必須フィールドが削除されました。
"required"が削除された後、"optional"は単に冗長なため、それも削除されました。