[インデックス 15767] ファイルの概要
このコミットは、Go言語の標準ライブラリ net
パッケージ内の TestDialTimeout
テストの信頼性を向上させるための変更です。具体的には、テストが不安定(deflake)になる問題を修正し、タイムアウト関連のテストがより確実に動作するように改善されています。
コミット
commit cdc642453bacfad6561eb4275bc55d752a9e92fb
Author: Albert Strasheim <fullung@gmail.com>
Date: Thu Mar 14 09:42:29 2013 -0700
net: deflake TestDialTimeout
Fixes #3867.
Fixes #3307.
R=bradfitz, dvyukov
CC=golang-dev
https://golang.org/cl/7735044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/cdc642453bacfad6561eb4275bc55d752a9e92fb
元コミット内容
net: deflake TestDialTimeout
このコミットの目的は、TestDialTimeout
テストの不安定性(flakiness)を解消することです。具体的には、GoのIssue #3867 と #3307 を修正します。
変更の背景
TestDialTimeout
は、ネットワーク接続のタイムアウト処理をテストするためのものです。しかし、このテストは実行環境やタイミングによって失敗することがあり、不安定な状態でした。このような不安定なテストは「flaky test」と呼ばれ、CI/CDパイプラインの信頼性を損ない、開発者が実際のバグとテストの不安定性を区別するのを困難にします。
コミットメッセージに記載されているIssue #3867と#3307は、このテストの不安定性に関連するものです。これらのIssueは、TestDialTimeout
が特定の条件下でタイムアウトを正しく検出できない、またはテストが意図しない挙動を示すことを報告していたと考えられます。
具体的な不安定性の原因としては、以下のようなものが考えられます。
- バックログの競合: サーバー側のリスナーが受け入れられる接続のバックログサイズが小さすぎると、クライアントからの接続要求がすぐに拒否され、タイムアウトの挙動が不安定になる可能性があります。
- テストのタイミング: ネットワーク操作は非同期であり、テストコードがネットワークイベントの発生を正確に予測できない場合、タイムアウトの検出が早すぎたり遅すぎたりして、テストが失敗することがあります。
- リソースの枯渇: テスト中に多数の接続を試行する場合、一時的なポートの枯渇やOSのネットワークスタックの制限により、予期せぬエラーが発生する可能性があります。
このコミットは、これらの潜在的な問題に対処し、TestDialTimeout
がより堅牢で信頼性の高いテストとなることを目指しています。
前提知識の解説
Go言語のテスト
Go言語には、標準でテストフレームワークが組み込まれています。テストファイルは通常、テスト対象のファイルと同じディレクトリに _test.go
というサフィックスを付けて配置されます。テスト関数は Test
で始まり、*testing.T
型の引数を取ります。t.Fatal()
や t.Errorf()
などのメソッドを使ってテストの失敗を報告します。
ネットワークプログラミングの基本
- ソケット (Socket): ネットワーク通信のエンドポイント。IPアドレスとポート番号の組み合わせで識別されます。
- リスナー (Listener): サーバー側で、特定のポートでクライアントからの接続要求を待ち受けるエンティティ。
- バックログ (Backlog): リスナーが同時に受け入れ可能な、まだ
Accept
されていない接続要求のキューの最大サイズ。このサイズを超えると、新たな接続要求は拒否されることがあります。 - ダイヤル (Dial): クライアント側で、特定のネットワークアドレス(IPアドレスとポート)に対して接続を確立しようとする操作。
- タイムアウト (Timeout): ネットワーク操作(接続、読み書きなど)が指定された時間内に完了しない場合に、その操作を中断するメカニズム。
Flaky Test (不安定なテスト)
Flaky testとは、同じコードに対して同じテストを実行しても、成功したり失敗したりするテストのことです。これは、テストが外部要因(ネットワークの遅延、並行処理のタイミング、リソースの競合など)に依存している場合に発生しやすく、テスト結果の信頼性を著しく低下させます。Flaky testの修正は、CI/CDパイプラインの安定性と開発者の生産性向上に不可欠です。
技術的詳細
このコミットの主要な変更点は、TestDialTimeout
テストにおけるリスナーのバックログサイズと、テストで試行する接続数の調整です。
-
listenerBacklog
の一時的な変更と復元:listenerBacklog
は、Goのnet
パッケージ内部で使用される、リスナーのバックログサイズを決定する変数です。このコミットでは、テストの開始時に元のlistenerBacklog
の値を保存し、テスト終了時にdefer
ステートメントを使って元の値に戻すようにしています。これにより、テストが他のテストに影響を与えないようにしています。 最も重要な変更は、listenerBacklog = 1
と設定している点です。これは、リスナーが同時に受け入れられる接続のバックログサイズを意図的に非常に小さく(1に)設定することを意味します。 -
numConns
の増加:numConns
は、テストで試行する接続の数を表します。変更前はlistenerBacklog + 10
でしたが、変更後はlistenerBacklog + 100
に増えています。listenerBacklog
が1に設定されているため、これは実質的に1 + 100 = 101
の接続を試行することを意味します。
これらの変更の組み合わせが、テストの不安定性を解消する鍵となります。
-
listenerBacklog = 1
の意図: バックログを1に設定することで、リスナーは同時に1つの接続しかキューに保持できません。これにより、多数の接続要求が同時に来た場合、ほとんどの接続要求はすぐに拒否されるか、タイムアウトする可能性が高まります。これは、タイムアウトの挙動をより確実にトリガーし、テストがその挙動を正確に検証できるようにするためのものです。もしバックログが大きすぎると、接続要求がキューに残り、タイムアウトが発生する前に受け入れられてしまう可能性があり、テストが意図したタイムアウトを検証できなくなることがあります。 -
numConns
の増加の意図: 試行する接続数を10から100に増やすことで、テストはより多くの接続要求を同時に発行します。バックログが小さい(1)状態で多数の接続要求を送りつけることで、システムが接続を処理しきれなくなり、タイムアウトが発生するシナリオをより確実に再現できます。これにより、テストがタイムアウトの条件をより頻繁かつ確実に満たすようになり、不安定性が解消されると考えられます。
要するに、この変更は、意図的にネットワークの輻輳状態を作り出し、DialTimeout
が期待通りに機能するかを厳密にテストするためのものです。
コアとなるコードの変更箇所
--- a/src/pkg/net/dial_test.go
+++ b/src/pkg/net/dial_test.go
@@ -28,12 +28,18 @@ func newLocalListener(t *testing.T) Listener {
}
func TestDialTimeout(t *testing.T) {
+ origBacklog := listenerBacklog
+ defer func() {
+ listenerBacklog = origBacklog
+ }()
+ listenerBacklog = 1
+
ln := newLocalListener(t)
defer ln.Close()
errc := make(chan error)
- numConns := listenerBacklog + 10
+ numConns := listenerBacklog + 100
// TODO(bradfitz): It's hard to test this in a portable
// way. This is unfortunate, but works for now.
コアとなるコードの解説
-
origBacklog := listenerBacklog
:listenerBacklog
の現在の値をorigBacklog
という変数に保存しています。これは、テストが終了した後に元の状態に戻すための準備です。 -
defer func() { listenerBacklog = origBacklog }()
:defer
キーワードは、囲んでいる関数(この場合はTestDialTimeout
)がリターンする直前に、指定された関数を実行することを保証します。ここでは、保存しておいたorigBacklog
の値をlistenerBacklog
に戻す無名関数を登録しています。これにより、このテストがlistenerBacklog
の値を変更しても、他のテストやシステム全体に影響を与えないようにしています。 -
listenerBacklog = 1
:listenerBacklog
の値を1
に設定しています。これは、前述の通り、リスナーが同時に受け入れられる接続のバックログサイズを意図的に非常に小さくすることで、タイムアウトのシナリオをより確実に発生させるための重要な変更です。 -
numConns := listenerBacklog + 100
:numConns
の計算式が変更されています。以前はlistenerBacklog + 10
でしたが、listenerBacklog + 100
になっています。listenerBacklog
が1
に設定されているため、numConns
は1 + 100 = 101
となります。これにより、テスト中に同時に試行される接続の数が大幅に増加し、バックログが小さい状態でのタイムアウト発生を促進します。
これらの変更により、TestDialTimeout
は、ネットワークの輻輳状態をより確実にシミュレートし、DialTimeout
関数が期待通りにタイムアウトを処理できるかを検証できるようになります。
関連リンク
- Go Issue #3867: https://github.com/golang/go/issues/3867
- Go Issue #3307: https://github.com/golang/go/issues/3307 (CLへのリンクですが、Issue 3307も関連している可能性があります)
- Go CL 7735044: https://golang.org/cl/7735044
参考にした情報源リンク
- Go言語の公式ドキュメント (testingパッケージ): https://pkg.go.dev/testing
- Go言語の公式ドキュメント (netパッケージ): https://pkg.go.dev/net
- TCP backlog (Wikipedia): https://en.wikipedia.org/wiki/TCP_backlog
- Flaky tests (Martin Fowler): https://martinfowler.com/articles/flakyTests.html
- Go言語のIssueトラッカー (GitHub): https://github.com/golang/go/issues
- Go Code Review Comments (CL): https://go.dev/doc/contribute#code_reviews
- Go CL (Code Review) System: https://go.dev/doc/contribute#code_reviews (CLのリンク形式から推測)