[インデックス 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
ここで、
is
はtime.Now().Sub(start)
で計算される「実際の経過時間」です。should
はcheckTimeout
関数に渡される「期待される時間」です。
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 Gerrit Change-Id: https://golang.org/cl/6816085
参考にした情報源リンク
- Go言語の
time
パッケージに関するドキュメント: https://pkg.go.dev/time - Go言語の
testing
パッケージに関するドキュメント: https://pkg.go.dev/testing - Git diffの読み方に関する一般的な情報
- タイムアウトと許容誤差に関する一般的なプログラミングの概念
- Go Gerritのレビューページ (上記関連リンクと同じ)