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

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

このコミットは、Go言語のnetパッケージにおけるTestReadWriteDeadlineテストの信頼性を向上させるためのものです。具体的には、テストが短時間で実行される(testing.Short()がtrueの場合)際に、タイムアウトの上限値の検証を行わないように変更し、タイムアウトの許容範囲(デルタ)を拡大しています。さらに、Plan 9オペレーティングシステム上ではこのテストを無効にすることで、プラットフォーム固有の問題によるテストの失敗を防いでいます。

コミット

  • コミットハッシュ: a906f9aa86c4305a1247391244e52b17e555f723
  • Author: Alex Brainman alex.brainman@gmail.com
  • Date: Sun Nov 4 12:41:49 2012 +1100
  • コミットメッセージ:
    net: do not test TestReadWriteDeadline timeout upper bound during short test
    
    It also increases timeout deltas to allow for longer wait.
    Also disables this test on plan9.
    
    R=golang-dev, minux.ma
    CC=golang-dev
    https://golang.org/cl/6821062
    

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

https://github.com/golang/go/commit/a906f9aa86c4305a1247391244e52b17e555f723

元コミット内容

net: do not test TestReadWriteDeadline timeout upper bound during short test

It also increases timeout deltas to allow for longer wait.
Also disables this test on plan9.

R=golang-dev, minux.ma
CC=golang-dev
https://golang.org/cl/6821062

変更の背景

このコミットの背景には、TestReadWriteDeadlineテストが特定の条件下で不安定であったという問題があります。具体的には、以下の点が挙げられます。

  1. testing.Short()モードでのタイムアウト上限値の検証の厳しさ: Goのテストフレームワークには、テストを高速に実行するための「ショートモード」が存在します。このモードでは、時間のかかるテストやネットワークに依存するテストの一部がスキップされたり、より緩い条件で実行されたりすることがあります。TestReadWriteDeadlineはネットワークI/Oのタイムアウトをテストするため、環境によっては正確なタイムアウト時間を保証するのが難しい場合があります。特に、ショートモードではテスト環境のリソースが限られている場合や、システム負荷が高い場合に、期待されるタイムアウト時間よりもわずかに長くかかってしまい、テストが失敗する可能性がありました。このコミットは、ショートモードではタイムアウトの上限値の検証を緩和することで、このような不安定性を解消しようとしています。

  2. タイムアウトの許容範囲(デルタ)の不足: ネットワークI/Oのタイムアウトテストは、厳密な時間計測を伴うため、システムの状態(CPU負荷、ネットワーク遅延など)によって結果が変動しやすい性質があります。以前のdelta値(許容誤差)が小さすぎたため、わずかな遅延でもテストが失敗する原因となっていました。これを拡大することで、テストの堅牢性を高めています。

  3. Plan 9固有の問題: Plan 9オペレーティングシステムは、Goがサポートするプラットフォームの一つですが、そのI/Oキャンセルメカニズムが他のOS(Linux, Windowsなど)と異なる場合があります。TestReadWriteDeadlineはI/Oのキャンセル機能に依存しているため、Plan 9環境ではテストが正しく動作しない、あるいは常に失敗する可能性がありました。このコミットは、Plan 9上でのテストを完全に無効にすることで、このプラットフォーム固有の互換性の問題を回避しています。

これらの変更は、テストの信頼性を高め、CI/CDパイプラインでの不必要な失敗を減らすことを目的としています。

前提知識の解説

このコミットを理解するためには、以下のGo言語およびネットワークプログラミングに関する知識が必要です。

  1. Go言語のnetパッケージ:

    • Goの標準ライブラリの一部で、TCP/IP、UDP、UnixドメインソケットなどのネットワークI/O機能を提供します。
    • net.Connインターフェースは、ネットワーク接続の一般的な振る舞いを定義しており、ReadWriteCloseメソッドの他に、SetReadDeadlineSetWriteDeadlineSetDeadlineといったタイムアウト設定のためのメソッドを提供します。
    • これらのSet*Deadlineメソッドは、指定された時刻までにI/O操作が完了しない場合にエラー(通常はnet.OpErrorで、Timeout()メソッドがtrueを返す)を返すように設定します。
  2. testingパッケージとtesting.Short():

    • Goの標準テストフレームワークです。
    • testing.Short()関数は、テストが「ショートモード」で実行されているかどうかを返します。ショートモードは、go test -shortコマンドで有効にできます。
    • 開発者がCI環境や日常的な開発で高速にテストを実行したい場合に、時間のかかるテスト(例: ネットワークテスト、ファイルI/Oテスト、ベンチマーク)の一部をスキップしたり、簡略化したりするために使用されます。
  3. runtime.GOOS:

    • Goの標準ライブラリruntimeパッケージの一部で、プログラムが実行されているオペレーティングシステムの名前(例: "linux", "windows", "darwin", "plan9"など)を文字列で返します。
    • これを利用して、特定のOSに依存するコードやテストの挙動を条件分岐させることができます。
  4. time.Duration:

    • Goのtimeパッケージで定義されている型で、時間の長さを表します。ナノ秒単位で内部的に表現されます。
    • time.Millisecondtime.Secondなどの定数を使って、人間が読みやすい形で時間を指定できます。
  5. I/Oキャンセル:

    • ネットワークソケットなどのI/O操作中に、その操作を途中で中断するメカニズムです。
    • SetReadDeadlineSetWriteDeadlineを設定すると、指定されたデッドラインまでにデータが読み書きされない場合、Goランタイムは内部的にI/O操作をキャンセルし、タイムアウトエラーを返します。このキャンセルメカニズムはOSの機能に依存します。
  6. Plan 9:

    • ベル研究所で開発された分散オペレーティングシステムです。Go言語の開発者の一部がPlan 9の設計思想に影響を受けており、GoはPlan 9を公式にサポートする数少ない主要言語の一つです。
    • Plan 9のファイルシステムとI/Oモデルは、Unix系OSとは異なる特徴を持つため、特定のシステムコールやI/O操作の挙動が異なることがあります。

技術的詳細

このコミットは、主にsrc/pkg/net/timeout_test.gosrc/pkg/net/ipsock_plan9.goの2つのファイルに変更を加えています。

src/pkg/net/ipsock_plan9.goの変更

このファイルには、Plan 9環境でのネットワークソケットに関する実装が含まれています。

  • var canCancelIO = true // used for testing current package の追加:
    • これは、netパッケージのテスト(特にtimeout_test.go)が、現在のシステムでI/Oキャンセルが可能かどうかを判断するために使用するグローバル変数です。
    • Plan 9では、この変数をtrueとして初期化していますが、これはテストのコンテキストで利用されるものです。後述のtimeout_test.goでの変更と合わせて、Plan 9でのテストの挙動を制御します。
    • この変数は、テストが実行される環境でI/Oキャンセルがサポートされているかどうかのフラグとして機能します。

src/pkg/net/timeout_test.goの変更

このファイルは、ネットワークI/Oのタイムアウト機能をテストするためのものです。

  1. Plan 9でのテストのスキップ:

    	switch runtime.GOOS {
    	case "plan9":
    		t.Logf("skipping test on %q", runtime.GOOS)
    		return
    	}
    
    • TestReadWriteDeadline関数の冒頭に、runtime.GOOSが"plan9"である場合にテストをスキップするロジックが追加されました。
    • これにより、Plan 9環境でのI/Oキャンセルメカニズムの差異に起因するテストの失敗を根本的に回避します。t.Logfでスキップ理由をログに出力し、returnでテスト関数を終了します。
  2. readTimeoutwriteTimeoutの調整:

    -		readTimeout  = 100 * time.Millisecond
    -		writeTimeout = 200 * time.Millisecond
    +		readTimeout  = 50 * time.Millisecond
    +		writeTimeout = 250 * time.Millisecond
    
    • readTimeoutが100msから50msに短縮され、writeTimeoutが200msから250msに延長されました。
    • これは、テストの実行時間を最適化しつつ、より現実的なタイムアウトシナリオをカバーするための調整と考えられます。特にreadTimeoutの短縮は、テストがより早くタイムアウト条件に到達するように意図されている可能性があります。
  3. delta(許容誤差)の変更とtesting.Short()の利用:

    -		delta        = 40 * time.Millisecond
    +		if d < -30*time.Millisecond || !testing.Short() && 150*time.Millisecond < d {
    
    • 以前は固定のdelta変数(40ms)を使用していましたが、このコミットではcheckTimeout関数内の条件式が直接変更されました。
    • 新しい条件式は以下の2つの部分から構成されます。
      • d < -30*time.Millisecond: 実際のタイムアウト時間が期待値よりも30ms以上短い場合にエラーとします。これは、タイムアウトが早すぎる場合を検出します。
      • !testing.Short() && 150*time.Millisecond < d:
        • !testing.Short(): テストがショートモードで実行されていない場合(つまり、フルテストモードの場合)にのみ、この条件が適用されます。
        • 150*time.Millisecond < d: 実際のタイムアウト時間が期待値よりも150ms以上長い場合にエラーとします。
        • この変更の最も重要な点は、ショートモード(testing.Short()がtrueの場合)では、タイムアウトの上限値(150msより長い遅延)の検証を行わないようにしたことです。これにより、ショートモードでのテストの不安定性が解消されます。フルテストモードでは、より厳密なタイムアウトの検証が維持されます。

これらの変更により、TestReadWriteDeadlineはより堅牢になり、異なる実行環境やテストモードでの信頼性が向上しました。

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

src/pkg/net/ipsock_plan9.go

--- a/src/pkg/net/ipsock_plan9.go
+++ b/src/pkg/net/ipsock_plan9.go
@@ -24,6 +24,8 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
 	return false, false
 }
 
+var canCancelIO = true // used for testing current package
+
 // parsePlan9Addr parses address of the form [ip!]port (e.g. 127.0.0.1!80).\
 func parsePlan9Addr(s string) (ip IP, iport int, err error) {
 	addr := IPv4zero // address contains port only

src/pkg/net/timeout_test.go

--- a/src/pkg/net/timeout_test.go
+++ b/src/pkg/net/timeout_test.go
@@ -148,19 +148,24 @@ func TestTimeoutAccept(t *testing.T) {
 }
 
 func TestReadWriteDeadline(t *testing.T) {
+\tswitch runtime.GOOS {\n+\tcase "plan9":\n+\t\tt.Logf("skipping test on %q", runtime.GOOS)\n+\t\treturn\n+\t}\n+\n  if !canCancelIO {
  	t.Logf("skipping test on this system")
  	return
  }
  	const (
 -		readTimeout  = 100 * time.Millisecond
 -		writeTimeout = 200 * time.Millisecond
 -		delta        = 40 * time.Millisecond
 +		readTimeout  = 50 * time.Millisecond
 +		writeTimeout = 250 * time.Millisecond
  	)
  	checkTimeout := func(command string, start time.Time, should time.Duration) {
  	is := time.Now().Sub(start)
  	d := should - is
-		if d < -delta || delta < d {\n+		if d < -30*time.Millisecond || !testing.Short() && 150*time.Millisecond < d {\n  		t.Errorf("%s timeout test failed: is=%v should=%v\\n", command, is, should)\n  	}\n  }

コアとなるコードの解説

src/pkg/net/ipsock_plan9.goの変更点

  • var canCancelIO = trueの追加:
    • この変数は、netパッケージのテストがI/Oキャンセル機能の有無を判断するために使用されます。Plan 9環境では、この変数をtrueとすることで、テストがI/Oキャンセルを試みることを許可します。しかし、これはあくまでテスト内部でのフラグであり、実際のPlan 9のI/Oキャンセル挙動が他のOSと異なるため、timeout_test.goでPlan 9でのテスト自体をスキップする措置が取られています。この変数は、主にテストの条件分岐を簡潔にするためのものです。

src/pkg/net/timeout_test.goの変更点

  1. Plan 9でのテストスキップ:

    	switch runtime.GOOS {
    	case "plan9":
    		t.Logf("skipping test on %q", runtime.GOOS)
    		return
    	}
    
    • runtime.GOOSを使用して現在のOSを判定し、もしPlan 9であれば、TestReadWriteDeadlineテスト全体をスキップします。これは、Plan 9のI/Oキャンセルメカニズムが他のOSと異なり、このテストが意図した通りに動作しない、あるいは常に失敗する可能性があったためです。これにより、Plan 9環境でのテストの信頼性が向上します。
  2. readTimeoutwriteTimeoutの調整:

    		readTimeout  = 50 * time.Millisecond
    		writeTimeout = 250 * time.Millisecond
    
    • readTimeoutが短縮され、writeTimeoutが延長されました。これは、テストの実行時間を最適化しつつ、より現実的なタイムアウトシナリオをカバーするための調整です。特にreadTimeoutの短縮は、テストがより早くタイムアウト条件に到達するように意図されている可能性があります。
  3. checkTimeout関数内のタイムアウト検証ロジックの変更:

    		if d < -30*time.Millisecond || !testing.Short() && 150*time.Millisecond < d {
    
    • この行がこのコミットの最も重要な変更点です。
    • dは、実際のタイムアウト時間と期待されるタイムアウト時間の差(should - is)を表します。
    • d < -30*time.Millisecond: 実際のタイムアウトが期待値よりも30ミリ秒以上短い場合(つまり、タイムアウトが早すぎる場合)はエラーとします。これは、タイムアウトが意図せず早く発生した場合を捕捉します。
    • !testing.Short() && 150*time.Millisecond < d:
      • !testing.Short(): この条件は、テストがショートモードで実行されていない場合にのみ評価されます。
      • 150*time.Millisecond < d: 実際のタイムアウトが期待値よりも150ミリ秒以上長い場合(つまり、タイムアウトが遅すぎる場合)はエラーとします。
      • この組み合わせにより、ショートモードでテストを実行している場合(testing.Short()がtrueの場合)は、タイムアウトが遅すぎる場合の上限値チェック(150*time.Millisecond < d)がスキップされます。これにより、ショートモードでのテストの不安定性が解消され、テストがより堅牢になります。フルテストモードでは、引き続き厳密なタイムアウトの検証が行われます。

これらの変更は、Goのnetパッケージのタイムアウトテストが、様々な環境やテストモードでより信頼性高く動作するようにするための重要な改善です。

関連リンク

参考にした情報源リンク

  • コミット情報: /home/orange/Project/comemo/commit_data/14311.txt
  • Go言語の公式ドキュメント (pkg.go.dev)
  • Go言語のソースコード (GitHub)
  • 一般的なネットワークプログラミングとテストの知識