Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 17949] ファイルの概要

このコミットは、Go言語の標準ライブラリsrc/pkg/syscall/route_bsd.goファイルに対する変更です。このファイルは、BSD系のオペレーティングシステム(FreeBSD, OpenBSD, NetBSDなど)におけるネットワークルーティングメッセージの処理に関連するシステムコール(syscall)を扱っています。具体的には、カーネルから受け取ったルーティングメッセージ(RoutingMessage)をパース(解析)する機能を提供します。

コミット

commit 517e49eb290a454791034eb692b696c347f7d74e
Author: Joel Sing <jsing@google.com>
Date:   Wed Dec 11 00:03:46 2013 +1100

    syscall: skip routing messages with mismatched version

    Skip routing messages with a mismatched version, rather than failing
    and returning EINVAL. Only return EINVAL if we were unable to parse
    any of the routing messages (presumably due to a version mismatch).

    R=golang-dev, bradfitz
    CC=golang-dev
    https://golang.org/cl/30340043

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/517e49eb290a454791034eb692b696c347f7d74e

元コミット内容

このコミットの目的は、ルーティングメッセージをパースする際に、バージョンが一致しないメッセージを即座にEINVALエラーで失敗させるのではなく、スキップするように変更することです。EINVALエラーは、ルーティングメッセージのどれもパースできなかった場合(おそらくバージョン不一致が原因)にのみ返されるべきである、という方針転換が示されています。

変更の背景

以前の実装では、ParseRoutingMessage関数が複数のルーティングメッセージを含むバイトスライスを受け取った際、その中の最初のメッセージのバージョンが期待されるRTM_VERSIONと一致しない場合、即座にEINVAL(Invalid argument)エラーを返していました。これは、複数のルーティングメッセージが連結されて送られてくるシナリオにおいて、一部のメッセージが異なるバージョンを持つ場合に問題を引き起こす可能性がありました。

例えば、カーネルが異なるバージョンのルーティングメッセージを混在させて送信する、あるいは、アプリケーションが複数のルーティングメッセージを処理する際に、一部のメッセージが古い(または新しい)バージョンであるといった状況が考えられます。このような場合、以前の実装では、有効なメッセージが後続に存在しても、最初のバージョン不一致で処理が中断されてしまい、システムがルーティング情報を完全に取得できない、あるいは不必要にエラーを返すという非効率性や堅牢性の欠如がありました。

このコミットの背景には、より堅牢で柔軟なルーティングメッセージのパース処理を実現し、部分的なバージョン不一致があっても可能な限り多くの有効なメッセージを処理できるようにするという意図があります。これにより、Goアプリケーションがネットワークルーティング情報を扱う際の信頼性と互換性が向上します。

前提知識の解説

このコミットを理解するためには、以下の前提知識が必要です。

  1. システムコール (syscall): オペレーティングシステムが提供するサービスをプログラムから利用するためのインターフェースです。ファイル操作、ネットワーク通信、プロセス管理など、OSの低レベルな機能にアクセスするために使用されます。Go言語のsyscallパッケージは、これらのOS固有の機能へのアクセスを提供します。

  2. ルーティングメッセージ (Routing Messages): BSD系のUnixライクなシステム(FreeBSD, OpenBSD, NetBSDなど)では、カーネルがネットワークルーティングテーブルの変更やネットワークインターフェースの状態変化などの情報を、ユーザー空間のプロセスにルーティングメッセージとして通知します。これらのメッセージは特定の構造を持ち、ネットワークデーモン(例: routed, zebra)がルーティング情報を管理するために利用します。

  3. RTM_VERSION: ルーティングメッセージのプロトコルバージョンを示す定数です。ルーティングメッセージのヘッダに含まれており、メッセージの構造や意味を解釈するために使用されます。バージョンが一致しない場合、メッセージのパース方法が異なる可能性があるため、通常は処理を中断するか、異なる方法で処理する必要があります。

  4. EINVAL: Unix系のシステムコールが返すエラーコードの一つで、「Invalid argument」(不正な引数)を意味します。関数に渡された引数が無効である場合に返されます。この文脈では、ルーティングメッセージのバージョンが不正である、あるいはメッセージ自体が破損しているといった状況で返されることが想定されます。

  5. unsafe.Pointer: Go言語のunsafeパッケージに含まれる型で、任意の型のポインタを保持できる特殊なポインタです。Goの型安全性をバイパスして、メモリを直接操作することを可能にします。このコミットでは、バイトスライス[]byteをルーティングメッセージの構造体anyMessageにキャストするために使用されています。これは、バイト列を特定の構造体として解釈する際に一般的に用いられる手法ですが、Goの型システムを迂回するため、注意深く使用する必要があります。

  6. バイトスライス ([]byte) と構造体 (struct) の変換: ネットワークから受信したデータは通常バイト列として扱われます。これをGoのプログラムで意味のある情報として処理するためには、バイト列を定義された構造体(この場合はルーティングメッセージのヘッダやペイロードを表す構造体)に変換する必要があります。unsafe.Pointerunsafe.Sizeofunsafe.Offsetofなどの関数を組み合わせて、バイトスライスを構造体として解釈するテクニックが用いられます。

技術的詳細

変更はsrc/pkg/syscall/route_bsd.goファイルのParseRoutingMessage関数に集中しています。この関数は、バイトスライスbを受け取り、その中に含まれる複数のルーティングメッセージをパースして、[]RoutingMessageの形で返します。

変更前は、forループ内で各メッセージを処理する際に、any.Version != RTM_VERSIONという条件でバージョンチェックを行い、バージョンが一致しない場合は即座にreturn nil, EINVALとしていました。これは、メッセージストリームの途中でバージョン不一致のメッセージが見つかった場合でも、それ以降のメッセージの処理を完全に停止させてしまう挙動でした。

変更後は、この挙動が以下のように修正されました。

  1. msgCount変数の導入: パースを試みたメッセージの総数をカウントするために、msgCountという新しい変数が導入されました。これは、ループが何回実行されたか(つまり、何個のメッセージの先頭を読み取ろうとしたか)を追跡します。

  2. バージョン不一致時のスキップ: if any.Version != RTM_VERSIONの条件が真(バージョン不一致)の場合、以前はreturn nil, EINVALでしたが、変更後はb = b[any.Msglen:]continueが実行されます。

    • b = b[any.Msglen:]: 現在のメッセージの長さ(any.Msglen)だけバイトスライスbを進めます。これにより、バージョン不一致のメッセージをスキップし、次のメッセージの先頭にポインタを移動させます。
    • continue: ループの次のイテレーションに進み、次のメッセージのパースを試みます。 この変更により、バージョン不一致のメッセージは無視され、後続のメッセージのパースが継続されるようになります。
  3. EINVALを返す条件の変更: ループが終了した後、つまりすべてのバイトスライスが処理された後に、EINVALを返す新しい条件が追加されました。

    // We failed to parse any of the messages - version mismatch?
    if msgCount > 0 && len(msgs) == 0 {
    	return nil, EINVAL
    }
    

    この条件は、以下の状況でEINVALを返します。

    • msgCount > 0: 少なくとも1つ以上のメッセージのパースを試みた。
    • len(msgs) == 0: しかし、結果として有効なルーティングメッセージが1つもパースできなかった。 このロジックは、「メッセージのパースを試みたが、どれも成功しなかった(おそらく全体的なバージョン不一致やデータ破損のため)」という場合にのみEINVALを返すことを意味します。これにより、部分的なバージョン不一致で即座にエラーになるのではなく、本当に処理すべきメッセージが一つもなかった場合にのみエラーを返す、より堅牢な挙動が実現されます。

この変更は、ネットワークプロトコルやシステムメッセージのパースにおいて一般的な「フォールトトレランス」の考え方を導入しています。つまり、一部の不正なデータがあっても全体が失敗するのではなく、可能な限り有効なデータを抽出し、処理を継続しようとするアプローチです。

コアとなるコードの変更箇所

src/pkg/syscall/route_bsd.goファイルのParseRoutingMessage関数内の以下の部分が変更されました。

--- a/src/pkg/syscall/route_bsd.go
+++ b/src/pkg/syscall/route_bsd.go
@@ -199,14 +199,21 @@ func (m *InterfaceAddrMessage) sockaddr() (sas []Sockaddr) {
 // ParseRoutingMessage parses b as routing messages and returns the
 // slice containing the RoutingMessage interfaces.
 func ParseRoutingMessage(b []byte) (msgs []RoutingMessage, err error) {
+\tmsgCount := 0 // 追加
 \tfor len(b) >= anyMessageLen {\
+\t\tmsgCount++ // 追加
 \t\tany := (*anyMessage)(unsafe.Pointer(&b[0]))\
 \t\tif any.Version != RTM_VERSION {\
-\t\t\treturn nil, EINVAL // 変更前: 即座にエラーを返す
+\t\t\tb = b[any.Msglen:] // 変更後: メッセージをスキップ
+\t\t\tcontinue // 変更後: 次のメッセージへ
 \t\t}\
 \t\tmsgs = append(msgs, any.toRoutingMessage(b))\
 \t\tb = b[any.Msglen:]\
 \t}\
+\t// We failed to parse any of the messages - version mismatch? // 追加
+\tif msgCount > 0 && len(msgs) == 0 { // 追加
+\t\treturn nil, EINVAL // 追加
+\t}
 \treturn msgs, nil
 }\

コアとなるコードの解説

ParseRoutingMessage関数は、バイトスライスbをルーティングメッセージのシーケンスとして解釈します。

  1. msgCount := 0: 関数冒頭でmsgCountが0で初期化されます。これは、この関数が処理を試みたルーティングメッセージの数を追跡するためのカウンターです。

  2. for len(b) >= anyMessageLen: このループは、残りのバイトスライスbが少なくとも1つのルーティングメッセージの最小長(anyMessageLen)以上である限り続きます。これにより、バイトスライスが尽きるまで、または有効なメッセージの先頭が見つからなくなるまで処理が繰り返されます。

  3. msgCount++: ループの各イテレーションの開始時にmsgCountがインクリメントされます。これは、現在処理しようとしているバイトスライスが、潜在的に新しいルーティングメッセージの開始点であることを示します。

  4. any := (*anyMessage)(unsafe.Pointer(&b[0])): unsafe.Pointerを使用して、現在のバイトスライスbの先頭をanyMessage構造体へのポインタとして解釈します。anyMessageはルーティングメッセージの共通ヘッダ部分を表す構造体であり、バージョン情報(Versionフィールド)やメッセージ長(Msglenフィールド)を含んでいます。

  5. if any.Version != RTM_VERSION: ここで、現在解釈しているメッセージのバージョンが、期待されるRTM_VERSIONと一致するかどうかをチェックします。

    • 変更前: バージョンが一致しない場合、即座にnilEINVALエラーを返して関数を終了していました。
    • 変更後: バージョンが一致しない場合でも、b = b[any.Msglen:]によって現在のメッセージの長さ分だけバイトスライスbを進め、continueによって次のメッセージのパースを試みます。これにより、不正なバージョンのメッセージをスキップし、後続のメッセージの処理を継続します。
  6. msgs = append(msgs, any.toRoutingMessage(b)): バージョンが一致した場合、any.toRoutingMessage(b)を呼び出して、現在のメッセージをより具体的なRoutingMessageインターフェースの型に変換し、結果のスライスmsgsに追加します。

  7. b = b[any.Msglen:]: 現在のメッセージが正常にパースされた場合、そのメッセージの長さ分だけバイトスライスbを進め、次のメッセージの先頭にポインタを移動させます。

  8. 新しいエラーチェック (if msgCount > 0 && len(msgs) == 0): forループが終了した後、この新しい条件が評価されます。

    • msgCount > 0: これは、関数が少なくとも1回はメッセージのパースを試みたことを意味します。つまり、入力バイトスライスbが空ではなかったということです。
    • len(msgs) == 0: これは、パースを試みたにもかかわらず、最終的に有効なルーティングメッセージが1つもmsgsスライスに追加されなかったことを意味します。 この両方の条件が真である場合、つまり「何かメッセージがあったが、どれも有効にパースできなかった」という状況で、初めてreturn nil, EINVALが実行されます。これにより、部分的なバージョン不一致ではなく、全体的なパースの失敗(例えば、すべてのメッセージが不正なバージョンであった場合など)に対してのみEINVALが返されるようになります。

この変更により、ParseRoutingMessage関数は、ルーティングメッセージストリームの堅牢性が向上し、部分的なバージョン不一致があっても可能な限り多くの有効なメッセージを抽出できるようになりました。

関連リンク

参考にした情報源リンク

このコミットは、Go言語の標準ライブラリsrc/pkg/syscall/route_bsd.goファイルに対する変更です。このファイルは、BSD系のオペレーティングシステム(FreeBSD, OpenBSD, NetBSDなど)におけるネットワークルーティングメッセージの処理に関連するシステムコール(syscall)を扱っています。具体的には、カーネルから受け取ったルーティングメッセージ(RoutingMessage)をパース(解析)する機能を提供します。

コミット

commit 517e49eb290a454791034eb692b696c347f7d74e
Author: Joel Sing <jsing@google.com>
Date:   Wed Dec 11 00:03:46 2013 +1100

    syscall: skip routing messages with mismatched version

    Skip routing messages with a mismatched version, rather than failing
    and returning EINVAL. Only return EINVAL if we were unable to parse
    any of the routing messages (presumably due to a version mismatch).

    R=golang-dev, bradfitz
    CC=golang-dev
    https://golang.org/cl/30340043

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/517e49eb290a454791034eb692b696c347f7d74e

元コミット内容

このコミットの目的は、ルーティングメッセージをパースする際に、バージョンが一致しないメッセージを即座にEINVALエラーで失敗させるのではなく、スキップするように変更することです。EINVALエラーは、ルーティングメッセージのどれもパースできなかった場合(おそらくバージョン不一致が原因)にのみ返されるべきである、という方針転換が示されています。

変更の背景

以前の実装では、ParseRoutingMessage関数が複数のルーティングメッセージを含むバイトスライスを受け取った際、その中の最初のメッセージのバージョンが期待されるRTM_VERSIONと一致しない場合、即座にEINVAL(Invalid argument)エラーを返していました。これは、複数のルーティングメッセージが連結されて送られてくるシナリオにおいて、一部のメッセージが異なるバージョンを持つ場合に問題を引き起こす可能性がありました。

例えば、カーネルが異なるバージョンのルーティングメッセージを混在させて送信する、あるいは、アプリケーションが複数のルーティングメッセージを処理する際に、一部のメッセージが古い(または新しい)バージョンであるといった状況が考えられます。このような場合、以前の実装では、有効なメッセージが後続に存在しても、最初のバージョン不一致で処理が中断されてしまい、システムがルーティング情報を完全に取得できない、あるいは不必要にエラーを返すという非効率性や堅牢性の欠如がありました。

このコミットの背景には、より堅牢で柔軟なルーティングメッセージのパース処理を実現し、部分的なバージョン不一致があっても可能な限り多くの有効なメッセージを処理できるようにするという意図があります。これにより、Goアプリケーションがネットワークルーティング情報を扱う際の信頼性と互換性が向上します。

前提知識の解説

このコミットを理解するためには、以下の前提知識が必要です。

  1. システムコール (syscall): オペレーティングシステムが提供するサービスをプログラムから利用するためのインターフェースです。ファイル操作、ネットワーク通信、プロセス管理など、OSの低レベルな機能にアクセスするために使用されます。Go言語のsyscallパッケージは、これらのOS固有の機能へのアクセスを提供します。

  2. ルーティングメッセージ (Routing Messages): BSD系のUnixライクなシステム(FreeBSD, OpenBSD, NetBSDなど)では、カーネルがネットワークルーティングテーブルの変更やネットワークインターフェースの状態変化などの情報を、ユーザー空間のプロセスにルーティングメッセージとして通知します。これらのメッセージは特定の構造を持ち、ネットワークデーモン(例: routed, zebra)がルーティング情報を管理するために利用します。

  3. RTM_VERSION: ルーティングメッセージのプロトコルバージョンを示す定数です。ルーティングメッセージのヘッダに含まれており、メッセージの構造や意味を解釈するために使用されます。バージョンが一致しない場合、メッセージの構造や意味が異なる可能性があるため、通常は処理を中断するか、異なる方法で処理する必要があります。

  4. EINVAL: Unix系のシステムコールが返すエラーコードの一つで、「Invalid argument」(不正な引数)を意味します。関数に渡された引数が無効である場合に返されます。この文脈では、ルーティングメッセージのバージョンが不正である、あるいはメッセージ自体が破損しているといった状況で返されることが想定されます。

  5. unsafe.Pointer: Go言語のunsafeパッケージに含まれる型で、任意の型のポインタを保持できる特殊なポインタです。Goの型安全性をバイパスして、メモリを直接操作することを可能にします。このコミットでは、バイトスライス[]byteをルーティングメッセージの構造体anyMessageにキャストするために使用されています。これは、バイト列を特定の構造体として解釈する際に一般的に用いられる手法ですが、Goの型システムを迂回するため、注意深く使用する必要があります。

  6. バイトスライス ([]byte) と構造体 (struct) の変換: ネットワークから受信したデータは通常バイト列として扱われます。これをGoのプログラムで意味のある情報として処理するためには、バイト列を定義された構造体(この場合はルーティングメッセージのヘッダやペイロードを表す構造体)に変換する必要があります。unsafe.Pointerunsafe.Sizeofunsafe.Offsetofなどの関数を組み合わせて、バイトスライスを構造体として解釈するテクニックが用いられます。

技術的詳細

変更はsrc/pkg/syscall/route_bsd.goファイルのParseRoutingMessage関数に集中しています。この関数は、バイトスライスbを受け取り、その中に含まれる複数のルーティングメッセージをパースして、[]RoutingMessageの形で返します。

変更前は、forループ内で各メッセージを処理する際に、any.Version != RTM_VERSIONという条件でバージョンチェックを行い、バージョンが一致しない場合は即座にreturn nil, EINVALとしていました。これは、メッセージストリームの途中でバージョン不一致のメッセージが見つかった場合でも、それ以降のメッセージの処理を完全に停止させてしまう挙動でした。

変更後は、この挙動が以下のように修正されました。

  1. msgCount変数の導入: パースを試みたメッセージの総数をカウントするために、msgCountという新しい変数が導入されました。これは、ループが何回実行されたか(つまり、何個のメッセージの先頭を読み取ろうとしたか)を追跡します。

  2. バージョン不一致時のスキップ: if any.Version != RTM_VERSIONの条件が真(バージョン不一致)の場合、以前はreturn nil, EINVALでしたが、変更後はb = b[any.Msglen:]continueが実行されます。

    • b = b[any.Msglen:]: 現在のメッセージの長さ(any.Msglen)だけバイトスライスbを進めます。これにより、バージョン不一致のメッセージをスキップし、次のメッセージの先頭にポインタを移動させます。
    • continue: ループの次のイテレーションに進み、次のメッセージのパースを試みます。 この変更により、バージョン不一致のメッセージは無視され、後続のメッセージのパースが継続されるようになります。
  3. EINVALを返す条件の変更: ループが終了した後、つまりすべてのバイトスライスが処理された後に、EINVALを返す新しい条件が追加されました。

    // We failed to parse any of the messages - version mismatch?
    if msgCount > 0 && len(msgs) == 0 {
    	return nil, EINVAL
    }
    

    この条件は、以下の状況でEINVALを返します。

    • msgCount > 0: 少なくとも1つ以上のメッセージのパースを試みた。
    • len(msgs) == 0: しかし、結果として有効なルーティングメッセージが1つもパースできなかった。 このロジックは、「メッセージのパースを試みたが、どれも成功しなかった(おそらく全体的なバージョン不一致やデータ破損のため)」という場合にのみEINVALを返すことを意味します。これにより、部分的なバージョン不一致で即座にエラーになるのではなく、本当に処理すべきメッセージが一つもなかった場合にのみエラーを返す、より堅牢な挙動が実現されます。

この変更は、ネットワークプロトコルやシステムメッセージのパースにおいて一般的な「フォールトトレランス」の考え方を導入しています。つまり、一部の不正なデータがあっても全体が失敗するのではなく、可能な限り有効なデータを抽出し、処理を継続しようとするアプローチです。

コアとなるコードの変更箇所

src/pkg/syscall/route_bsd.goファイルのParseRoutingMessage関数内の以下の部分が変更されました。

--- a/src/pkg/syscall/route_bsd.go
+++ b/src/pkg/syscall/route_bsd.go
@@ -199,14 +199,21 @@ func (m *InterfaceAddrMessage) sockaddr() (sas []Sockaddr) {
 // ParseRoutingMessage parses b as routing messages and returns the
 // slice containing the RoutingMessage interfaces.
 func ParseRoutingMessage(b []byte) (msgs []RoutingMessage, err error) {
+\tmsgCount := 0 // 追加
 \tfor len(b) >= anyMessageLen {\
+\t\tmsgCount++ // 追加
 \t\tany := (*anyMessage)(unsafe.Pointer(&b[0]))\
 \t\tif any.Version != RTM_VERSION {\
-\t\t\treturn nil, EINVAL // 変更前: 即座にエラーを返す
+\t\t\tb = b[any.Msglen:] // 変更後: メッセージをスキップ
+\t\t\tcontinue // 変更後: 次のメッセージへ
 \t\t}\
 \t\tmsgs = append(msgs, any.toRoutingMessage(b))\
 \t\tb = b[any.Msglen:]\
 \t}\
+\t// We failed to parse any of the messages - version mismatch? // 追加
+\tif msgCount > 0 && len(msgs) == 0 { // 追加
+\t\treturn nil, EINVAL // 追加
+\t}
 \treturn msgs, nil
 }\

コアとなるコードの解説

ParseRoutingMessage関数は、バイトスライスbをルーティングメッセージのシーケンスとして解釈します。

  1. msgCount := 0: 関数冒頭でmsgCountが0で初期化されます。これは、この関数が処理を試みたルーティングメッセージの数を追跡するためのカウンターです。

  2. for len(b) >= anyMessageLen: このループは、残りのバイトスライスbが少なくとも1つのルーティングメッセージの最小長(anyMessageLen)以上である限り続きます。これにより、バイトスライスが尽きるまで、または有効なメッセージの先頭が見つからなくなるまで処理が繰り返されます。

  3. msgCount++: ループの各イテレーションの開始時にmsgCountがインクリメントされます。これは、現在処理しようとしているバイトスライスが、潜在的に新しいルーティングメッセージの開始点であることを示します。

  4. any := (*anyMessage)(unsafe.Pointer(&b[0])): unsafe.Pointerを使用して、現在のバイトスライスbの先頭をanyMessage構造体へのポインタとして解釈します。anyMessageはルーティングメッセージの共通ヘッダ部分を表す構造体であり、バージョン情報(Versionフィールド)やメッセージ長(Msglenフィールド)を含んでいます。

  5. if any.Version != RTM_VERSION: ここで、現在解釈しているメッセージのバージョンが、期待されるRTM_VERSIONと一致するかどうかをチェックします。

    • 変更前: バージョンが一致しない場合、即座にnilEINVALエラーを返して関数を終了していました。
    • 変更後: バージョンが一致しない場合でも、b = b[any.Msglen:]によって現在のメッセージの長さ分だけバイトスライスbを進め、continueによって次のメッセージのパースを試みます。これにより、不正なバージョンのメッセージをスキップし、後続のメッセージの処理を継続します。
  6. msgs = append(msgs, any.toRoutingMessage(b)): バージョンが一致した場合、any.toRoutingMessage(b)を呼び出して、現在のメッセージをより具体的なRoutingMessageインターフェースの型に変換し、結果のスライスmsgsに追加します。

  7. b = b[any.Msglen:]: 現在のメッセージが正常にパースされた場合、そのメッセージの長さ分だけバイトスライスbを進め、次のメッセージの先頭にポインタを移動させます。

  8. 新しいエラーチェック (if msgCount > 0 && len(msgs) == 0): forループが終了した後、この新しい条件が評価されます。

    • msgCount > 0: これは、関数が少なくとも1回はメッセージのパースを試みたことを意味します。つまり、入力バイトスライスbが空ではなかったということです。
    • len(msgs) == 0: これは、パースを試みたにもかかわらず、最終的に有効なルーティングメッセージが1つもmsgsスライスに追加されなかったことを意味します。 この両方の条件が真である場合、つまり「何かメッセージがあったが、どれも有効にパースできなかった」という状況で、初めてreturn nil, EINVALが実行されます。これにより、部分的なバージョン不一致ではなく、全体的なパースの失敗(例えば、すべてのメッセージが不正なバージョンであった場合など)に対してのみEINVALが返されるようになります。

この変更により、ParseRoutingMessage関数は、ルーティングメッセージストリームの堅牢性が向上し、部分的なバージョン不一致があっても可能な限り多くの有効なメッセージを抽出できるようになりました。

関連リンク

参考にした情報源リンク