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

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

このコミットは、Go言語の標準ライブラリnetパッケージ内のタイムアウトテストtimeout_test.goにおける、タイムアウトの許容誤差(slack)計算のロジックを修正するものです。具体的には、実際の経過時間と期待される時間の差分を計算する際の符号が修正され、テストの正確性が向上しました。

コミット

commit 834028d5270794c02a2744bb778cde3c1c8effe8
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Sun Nov 4 18:07:59 2012 +0800

    net: fix timeout slack calculation
    
    R=alex.brainman
    CC=golang-dev
    https://golang.org/cl/6816085

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

https://github.com/golang/go/commit/834028d5270794c02a2744bb778cde3c1c8effe8

元コミット内容

net: fix timeout slack calculation

R=alex.brainman
CC=golang-dev
https://golang.org/cl/6816085

変更の背景

この変更は、netパッケージのタイムアウトテストTestReadWriteDeadline内で使用されるcheckTimeoutヘルパー関数における、タイムアウトの許容誤差(slack)の計算が誤っていたために行われました。以前の計算式d := should - isでは、実際の経過時間(is)と期待される時間(should)の差分dの符号が、直感的な「どれだけ期待値からずれたか」という解釈と逆になっていました。これにより、テストが意図しない結果を報告したり、特定のタイムアウト条件を正しく検出できない可能性がありました。

テストの目的は、ネットワーク操作が設定されたデッドライン(期限)内に完了するか、または許容範囲内でタイムアウトするかを確認することです。この許容誤差の計算が正しくないと、テストが不安定になったり、実際のバグを見逃したりするリスクがありました。このコミットは、dが「実際の経過時間 - 期待される時間」という、より標準的で理解しやすい偏差を表すように修正することで、テストの信頼性を向上させることを目的としています。

前提知識の解説

  • time.Duration: Go言語における時間の長さを表す型です。ミリ秒、秒、分、時間などの単位で時間を表現できます。
  • time.Time: 特定の時点を表す型です。
  • time.Now(): 現在のローカル時刻をtime.Time型で返します。
  • time.Time.Sub(t time.Time): tから現在のtime.Timeまでの期間をtime.Durationとして返します。つまり、end.Sub(start)startからendまでの経過時間を計算します。
  • タイムアウト(Timeout): ネットワーク通信やI/O操作において、処理が完了するまでに許容される最大時間のことです。この時間を超えると、操作は中断され、エラーが返されます。
  • 許容誤差(Slack): タイムアウトテストにおいて、厳密な期待値からのわずかなずれを許容する範囲のことです。システム負荷やスケジューリングのばらつきにより、正確に期待値通りの時間で処理が完了することは稀であるため、ある程度の誤差は許容されます。
  • testing.Short(): Goのテストフレームワークtestingパッケージの関数で、go test -shortフラグが指定されている場合にtrueを返します。これにより、時間がかかるテストの一部をスキップしたり、テストの厳密さを緩和したりすることができます。
  • t.Errorf(format string, args ...interface{}): テスト中にエラーが発生したことを報告し、テストを失敗としてマークしますが、テストの実行は継続します。

技術的詳細

このコミットの核心は、checkTimeout関数内のd変数の計算方法の変更です。

元のコード:

d := should - is

修正後のコード:

d := is - should

ここで、

  • istime.Now().Sub(start) で計算される「実際の経過時間」です。
  • shouldcheckTimeout 関数に渡される「期待される時間」です。

dは、実際の経過時間と期待される時間の「差分」を表します。

元の計算 d := should - is の場合:

  • もし is(実際の時間)が should(期待される時間)よりも長かった場合(つまり、タイムアウトが遅れた場合)、should - is は負の値になります。 例: should = 100ms, is = 150ms => d = 100 - 150 = -50ms
  • もし is(実際の時間)が should(期待される時間)よりも短かった場合(つまり、タイムアウトが早すぎた場合)、should - is は正の値になります。 例: should = 100ms, is = 50ms => d = 100 - 50 = 50ms

この場合、dの符号が直感的な「遅延」や「早期終了」と逆になります。

修正後の計算 d := is - should の場合:

  • もし is(実際の時間)が should(期待される時間)よりも長かった場合(つまり、タイムアウトが遅れた場合)、is - should は正の値になります。 例: should = 100ms, is = 150ms => d = 150 - 100 = 50ms
  • もし is(実際の時間)が should(期待される時間)よりも短かった場合(つまり、タイムアウトが早すぎた場合)、is - should は負の値になります。 例: should = 100ms, is = 50ms => d = 50 - 100 = -50ms

この修正により、dが正の値であれば「期待よりも時間がかかった」、負の値であれば「期待よりも早く終わった」という、より直感的で一般的な偏差の表現になります。

テストの条件式は以下の通りです。

if d < -30*time.Millisecond || !testing.Short() && 150*time.Millisecond < d {
    t.Errorf("%s timeout test failed: is=%v should=%v\n", command, is, should)
}

この条件式は、dが許容範囲外である場合にエラーを報告します。

  • d < -30*time.Millisecond: dが-30ミリ秒より小さい場合。これは、実際の時間が期待される時間よりも30ミリ秒以上短かった場合(早すぎた場合)を検出します。
  • !testing.Short() && 150*time.Millisecond < d: go test -shortフラグが指定されていない場合に、dが150ミリ秒より大きい場合。これは、実際の時間が期待される時間よりも150ミリ秒以上長かった場合(遅すぎた場合)を検出します。

修正後のdの定義(is - should)と上記の条件式は、テストが「早すぎるタイムアウト」と「遅すぎるタイムアウト」の両方を正確に検出できるように整合性が取れています。

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

--- a/src/pkg/net/timeout_test.go
+++ b/src/pkg/net/timeout_test.go
@@ -164,7 +164,7 @@ func TestReadWriteDeadline(t *testing.T) {
 	)
 	checkTimeout := func(command string, start time.Time, should time.Duration) {
 		is := time.Now().Sub(start)
-		d := should - is
+		d := is - should
 		if d < -30*time.Millisecond || !testing.Short() && 150*time.Millisecond < d {
 			t.Errorf("%s timeout test failed: is=%v should=%v\n", command, is, should)
 		}

コアとなるコードの解説

変更はsrc/pkg/net/timeout_test.goファイル内のTestReadWriteDeadline関数内で定義されている匿名関数checkTimeoutの中にあります。

  • is := time.Now().Sub(start): この行は、start時刻から現在までの実際の経過時間(is)を計算しています。
  • d := is - should: この行が修正された部分です。dは、実際の経過時間isから期待される時間shouldを引いた差分を計算します。これにより、dが正であれば「期待よりも遅い」、負であれば「期待よりも早い」という直感的な意味を持つようになります。
  • if d < -30*time.Millisecond || !testing.Short() && 150*time.Millisecond < d: この条件式は、計算された差分dが許容範囲外であるかをチェックします。
    • d < -30*time.Millisecond: 実際の時間が期待される時間より30ミリ秒以上短い場合(早すぎる場合)。
    • !testing.Short() && 150*time.Millisecond < d: go test -shortモードでない場合に、実際の時間が期待される時間より150ミリ秒以上長い場合(遅すぎる場合)。 これらの条件のいずれかが真であれば、t.Errorfが呼び出され、テストが失敗したことを報告します。

この修正により、checkTimeout関数は、ネットワーク操作のタイムアウトが設定されたデッドラインに対して、許容される「遅延」または「早期完了」の範囲を正確に評価できるようになりました。

関連リンク

参考にした情報源リンク

  • Go言語のtimeパッケージに関するドキュメント: https://pkg.go.dev/time
  • Go言語のtestingパッケージに関するドキュメント: https://pkg.go.dev/testing
  • Git diffの読み方に関する一般的な情報
  • タイムアウトと許容誤差に関する一般的なプログラミングの概念
  • Go Gerritのレビューページ (上記関連リンクと同じ)