プロトバッファのゼロ値トラップ:デフォルト値がビジネスロジックの隠れた殺人者となる場合

米国株には3つの取引時間帯があり、それぞれ盤前、盤中、盤後です。データインターフェースは、可能な限り帯域幅を節約するために、数値増分方式でデータをプッシュします。初回送信時はフルデータですが、2回目以降はすべてのフィールドが差分更新となります。

なぜ最適な方法を使わないのか?いくつかのプロジェクトチームが関わっていて、すでに何年も稼働しているものもある。当方は新規の連携なので、できるだけ互換性を保つようにするしかない。

一連の問題

概要だけ見れば、まだ問題なさそうに見えるかもしれないが、システムアーキテクチャに問題を持ち込むと、一連の問題を引き起こす。先の問題を解決したと思ったら、また新しい問題が発生し、その問題は以前の問題が原因だ。

取引期間を識別できません。

既知ディスク上のフェーズ定義は、protobufで0と定義されていますが、データ受信時に増分プッシュであるため、ビジネス側はこの0がデフォルト値なのか、実際のビジネス値なのかを有効に識別できません。

平たく言うと、0を受け取るたびに、その0が新しい相場設定の値なのか、それともprotobufのデフォルト値なのかを判断できないということです。

オプション導入

Since protobuf release 3.15, proto3 supports using the optional keyword (just as in proto2) to give a scalar field presence information

グループ内の通信プロトコルは protobuf をベースにしていますが、過去の経緯から古いバージョンが採用されており、optional キーワードに対応していません。事情はよくわかっていると思いますが、protobufを基盤から導入しているため、プロジェクトは静的ライブラリとして配布され、そのためにはコンパイルチェーン全体をアップグレードする必要があります。このコストは非常に高いです。

GCCのバージョン問題

やっとの思いで計画を立てたものの、基盤に2つの異なるバージョンをリリースし、可能な限りprotobufの新バージョンのコンパイル依存関係の伝播を制御しようとしたのですが、コンパイル時にgccのバージョンが低く、protobufの新機能に対応していないことがわかりました。

チーム内でよく使用されるサーバーの種類:CentOS 7、CentOS 8。CentOS 7 のデフォルトの gcc バージョンは 4.8 で、CentOS 8 のデフォルトの gcc バージョンは 8.3 です。protobuf の新機能には gcc バージョンが 7.4 以上必要であるため、CentOS 7 ではサポートできません。

Bug 82461 - [7 Regression] Temporary required for brace-initializing (non-literal-type) member variable

最終、関連サービスのデプロイやコンパイルサーバーをCentOS 8に移行することで、この問題を解決しました。

合理な列挙

問題全体を振り返ると、実はもっと簡単で効率的な解決策があります。それは、列挙の定義を調整し、0からではなく1から番号付けすることです。これにより、デフォルト値とビジネス値を効果的に区別でき、上記の様々な問題を回避できます。

なぜ1から始めるのがもっと合理的だろうか?

protobufにおいて、列挙型のデフォルト値は常に0に固定されています。もしビジネス上の意味のある値を0として定義した場合(例えば「盤中」)、増分プッシュ時に受信した0がビジネスの値なのか、未設定のデフォルト値なのかをクライアント側で判断することができません。しかし、列挙型を1から開始して定義すれば、0を有意義でないデフォルト値や「未知」の状態として保持できるため、問題は容易に解決されます。

推奨される実践:

プロトバッファの列挙型を設計する際には、常に 0 を無意味なデフォルト値として定義してください(例:UNKNOWN または RESERVED)。 実際のビジネス値を1から割り当て、デフォルト値の0と区別するようにしてください。

この小さな調整により、取引セッションの識別問題を解決するだけでなく、将来のプロトコル設計のための貴重な経験となりました。

金融ITプログラマーのいじくり回しと日常のつぶやき
Hugo で構築されています。
テーマ StackJimmy によって設計されています。