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

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

このコミットは、Go言語の標準ライブラリ os/signal パッケージ内のテストの不安定性(flakiness)を解消するための変更です。具体的には、signal_test.go ファイルの TestStress 関数に time.Sleep を追加することで、テスト間の信号の漏洩を防ぎ、テストの信頼性を向上させています。

コミット

  • コミットハッシュ: fe017550ac3429d2fb0d808f581e7f7247a79767
  • Author: Dmitriy Vyukov dvyukov@google.com
  • Date: Mon Mar 11 22:31:34 2013 +0400
  • コミットメッセージ:
    os/signal: deflake test
    Fixes #4987.
    
    R=golang-dev, iant
    CC=golang-dev
    https://golang.org/cl/7713043
    

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

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

元コミット内容

os/signal: deflake test
Fixes #4987.

R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/7713043

変更の背景

このコミットの主な目的は、Go言語の os/signal パッケージにおけるテストの不安定性(flakiness)を解消することです。コミットメッセージには「Fixes #4987」と記載されており、これはGoのIssueトラッカーで報告された特定のバグや問題に対応していることを示唆しています。

Web検索の結果、直接的なIssue #4987の詳細は見つかりませんでしたが、os/signal パッケージのテストの不安定性に関する類似の報告(例: Go issue 31264)が存在します。これらの問題は一般的に、テストが非常に高速に実行される環境(特に並列テスト実行時、例: go test -cpu=1,2,4)において、あるテストで発生した信号が次のテストに「漏洩」し、予期せぬ失敗を引き起こすというものです。

具体的には、TestStress のような信号を多用するテストが終了した後、そのテストが送信した信号がシステムバッファに残存し、後続の TestSignal() のような別の信号関連テストが開始された際に、その残存信号を誤って受信してしまう可能性がありました。これにより、テストが本来成功すべき状況で失敗するという、再現性の低い「flaky」な挙動が発生していました。このコミットは、このような信号の漏洩によるテストの不安定性を軽減することを目的としています。

前提知識の解説

Flaky Test(不安定なテスト)

Flaky testとは、同じコードベースとテスト環境で実行しても、成功したり失敗したりするテストのことです。これは、テストの実行順序、並行処理、外部リソースへの依存、タイミングの問題など、非決定的な要因によって引き起こされます。Flaky testは開発者の信頼を損ない、CI/CDパイプラインの効率を低下させるため、ソフトウェア開発において避けるべき問題とされています。

Go言語の os/signal パッケージ

os/signal パッケージは、Goプログラムがオペレーティングシステムからのシグナル(例: SIGINTSIGTERMSIGUSR1など)を処理するための機能を提供します。これにより、プログラムは外部からのイベント(例: Ctrl+Cによる終了要求)に適切に応答したり、特定のシグナルを捕捉してカスタムの動作を実行したりすることができます。

time.Sleep() のテストにおける利用

time.Sleep() は、指定された期間だけ現在のゴルーチンを一時停止させる関数です。テストにおいて time.Sleep() を使用することは、一般的には非推奨とされています。なぜなら、テストの実行時間を不必要に長くしたり、環境依存のタイミングの問題を完全に解決できない可能性があるためです。しかし、本コミットのように、特定の並行処理やシステムイベントのタイミングに起因するレースコンディションを一時的に緩和し、テストの不安定性を解消するための「最後の手段」として用いられることがあります。これは、根本的なレースコンディションを解決するのではなく、発生確率を下げるための対症療法的なアプローチです。

技術的詳細

この変更は、os/signal パッケージの TestStress 関数に time.Sleep(10 * time.Millisecond) を追加することで、テストの不安定性を解消しています。

TestStress は、おそらく大量のシグナルを生成・処理することで、シグナルハンドリングの堅牢性をテストする関数です。このようなテストは、システムのリソースやスケジューリングに大きく依存するため、並列実行される他のテストとの間で競合状態(race condition)を引き起こしやすい性質があります。

特に、go test -cpu=1,2,4 のように複数のCPUコアでテストを並列実行する場合、TestStress が送信した SIGUSR1 などのシグナルが、テスト終了後もシステム内のシグナルキューやバッファに一時的に残存する可能性があります。この残存シグナルが、直後に実行される別のシグナル関連テスト(例: TestSignal())によって誤って捕捉されてしまうと、そのテストが予期せぬ動作をして失敗する原因となります。

time.Sleep(10 * time.Millisecond) を追加することで、TestStress が終了してから次のテストが開始されるまでの間に、わずかな遅延を設けています。この10ミリ秒の遅延は、システムが残存するシグナルを完全に処理し、シグナルキューをクリアするのに十分な時間を与えることを意図しています。これにより、後続のテストが「クリーンな状態」で開始され、前のテストからのシグナルの影響を受けずに実行できるようになり、テストの不安定性が軽減されます。

これは、根本的なシグナルハンドリングのロジックを変更するのではなく、テスト実行のタイミングを調整することで、特定の環境下でのレースコンディションの発生確率を下げるという、実用的なデフラグ(deflake)手法です。

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

--- a/src/pkg/os/signal/signal_test.go
+++ b/src/pkg/os/signal/signal_test.go
@@ -98,4 +98,8 @@ func TestStress(t *testing.T) {
 	close(done)
 	<-finished
 	<-finished
+	// When run with 'go test -cpu=1,2,4' SIGUSR1 from this test can slip
+	// into subsequent TestSignal() causing failure.
+	// Sleep for a while to reduce the possibility of the failure.
+	time.Sleep(10 * time.Millisecond)
 }

コアとなるコードの解説

変更は src/pkg/os/signal/signal_test.go ファイルの TestStress 関数内で行われています。

TestStress 関数は、テストの終了処理として done チャネルを閉じ、finished チャネルからの受信を2回行っています。これは、テスト内で起動されたゴルーチンがすべて完了するのを待つための一般的なパターンです。

追加された4行は以下の通りです。

	// When run with 'go test -cpu=1,2,4' SIGUSR1 from this test can slip
	// into subsequent TestSignal() causing failure.
	// Sleep for a while to reduce the possibility of the failure.
	time.Sleep(10 * time.Millisecond)
  • コメント: 追加されたコメントは、この time.Sleep が導入された理由を明確に説明しています。
    • go test -cpu=1,2,4 で実行された場合、このテストからの SIGUSR1 が後続の TestSignal() に漏洩し、失敗を引き起こす可能性がある。」
    • 「この失敗の可能性を減らすために、しばらくスリープする。」
  • time.Sleep(10 * time.Millisecond): これが実際の変更点です。TestStress 関数が完全に終了する直前に、10ミリ秒間実行を一時停止します。

このスリープは、TestStress が生成したシグナルがシステムによって完全に処理され、シグナルキューがクリアされるための「猶予期間」を提供します。これにより、次に実行されるテスト(特に TestSignal() のようなシグナルに敏感なテスト)が、前のテストの残存シグナルによって誤動作するのを防ぎ、テストの信頼性を向上させています。

関連リンク

参考にした情報源リンク