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

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

このコミットは、Go言語のnetパッケージにおけるTestDialTimeoutFDLeakテストの信頼性に関する問題を修正するものです。具体的には、ファイルディスクリプタのリークを検出するためのテストが、特定の条件下で不安定になる事象(Issue 4384)に対応しています。テスト内のmaxGoodConnectという変数の値を調整することで、テストの安定性を向上させています。

コミット

  • コミットハッシュ: 4766a35e7c4c00dd060313080f6d85e32c9aa970
  • Author: Dave Cheney dave@cheney.net
  • Date: Wed Dec 12 07:25:07 2012 +1100

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

https://github.com/golang/go/commit/4766a35e7c4c00dd060313080f6d85e32c9aa970

元コミット内容

    net: TestDialTimeoutFDLeak failure
    
    Fixes #4384.
    
    Implments the suggestion by rsc in comment 15, http://code.google.com/p/go/issues/detail?id=4384#c15
    
    An alternate suggestion would be to temporarily set GOMAXPROCS to 1 during this test.
    
    R=fullung, rsc
    CC=golang-dev
    https://golang.org/cl/6923046

変更の背景

このコミットは、Go言語のnetパッケージに含まれるTestDialTimeoutFDLeakというテストが、特定の環境や実行条件下で失敗する問題(Issue 4384)を解決するために行われました。TestDialTimeoutFDLeakは、ネットワーク接続のタイムアウト処理中にファイルディスクリプタ(FD)が適切に閉じられず、リークが発生しないことを検証するための重要なテストです。

しかし、このテストはlistenerBacklog + 5という動的な値に依存するmaxGoodConnectという変数の設定が原因で、不安定な挙動を示していました。listenerBacklogはシステムや環境によって異なる可能性があり、その結果、テストが期待通りの数の接続を確立できず、誤ってFDリークを報告したり、単にテストが失敗したりすることがありました。

この不安定性は、Goの標準ライブラリの品質保証において問題となるため、テストの信頼性を向上させる必要がありました。コミットメッセージには、rsc(Russ Cox)によるコメント15の提案が実装されたと明記されており、これはテストのロバスト性を高めるための具体的な解決策を示唆しています。

前提知識の解説

ファイルディスクリプタ (File Descriptor, FD)

ファイルディスクリプタは、Unix系オペレーティングシステムにおいて、プロセスが開いているファイルやソケットなどのI/Oリソースを識別するために使用される抽象的なハンドルです。プログラムがファイルを開いたり、ネットワーク接続を確立したりするたびに、カーネルは対応するファイルディスクリプタをプロセスに割り当てます。

ファイルディスクリプタリーク (File Descriptor Leak)

ファイルディスクリプタリークとは、プログラムがファイルディスクリプタを使い終わった後に適切に閉じないために、そのFDがシステムに解放されずに残り続ける状態を指します。FDは有限のリソースであり、リークが続くと、最終的にはプロセスが利用可能なFDの最大数(ulimit -nなどで設定される)に達し、「Too many open files (EMFILE)」エラーが発生します。これは、新しいファイルを開いたり、ネットワーク接続を確立したりできなくなることを意味し、アプリケーションのクラッシュやサービス停止につながる可能性があります。

特にネットワークプログラミングでは、多数の接続を扱うため、FDリークは深刻な問題となります。接続が確立されるたびにソケットがFDを消費し、閉じられないソケットが蓄積されると、システムリソースを枯渇させます。

net.Dialとタイムアウト

Go言語のnetパッケージは、ネットワークI/Oの基本的な機能を提供します。net.Dial関数は、指定されたネットワークアドレスへの接続を確立するために使用されます。ネットワーク接続は、様々な理由(例: サーバーが応答しない、ネットワークが混雑している)で時間がかかる場合があるため、タイムアウトを設定することが重要です。タイムアウトは、接続確立に許容される最大時間を指定し、その時間を超えた場合は接続試行を中止します。

TestDialTimeoutFDLeakテスト

TestDialTimeoutFDLeakは、netパッケージの内部テストであり、net.Dial関数がタイムアウトした場合にファイルディスクリプタがリークしないことを検証します。このテストは、多数の同時接続試行を行い、そのうちの一部がタイムアウトするように設計されています。テストの目的は、タイムアウトによって接続が中断された場合でも、関連するFDが適切に閉じられ、システムリソースが解放されることを確認することです。

技術的詳細

TestDialTimeoutFDLeakテストは、listenerBacklogという値を利用して、同時に確立される「良い」接続の数を制御していました。listenerBacklogは、TCPリスナーがキューに入れることができる保留中の接続の最大数を指します。元のコードでは、maxGoodConnectという変数がlistenerBacklog + 5と設定されていました。これは、リスナーのバックログサイズに少し余裕を持たせた数の接続が成功することを期待していました。

しかし、このアプローチには問題がありました。listenerBacklogの値はオペレーティングシステムやその設定によって異なり、必ずしも予測可能ではありません。例えば、Linuxではnet.core.somaxconnカーネルパラメータによって制御され、デフォルト値は128や4096など様々です。この変動性のため、listenerBacklog + 5という計算されたmaxGoodConnectの値が、テストが実際に成功させられる接続数と一致しないことがありました。

具体的には、テストがmaxGoodConnectで指定された数よりも少ない「良い」接続しか確立できなかった場合、テストはFDリークが発生したと誤って判断し、失敗していました。これは実際のFDリークではなく、テストのロジックが環境に依存しすぎていたために発生する不安定な失敗でした。

コミットメッセージにある「rsc in comment 15」の提案は、このmaxGoodConnectの値を固定値にすることで、テストの信頼性を高めるというものでした。これにより、テストは特定の環境設定に左右されず、常に同じ数の成功接続を期待するようになります。

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

--- a/src/pkg/net/dial_test.go
+++ b/src/pkg/net/dial_test.go
@@ -240,7 +240,8 @@ func TestDialTimeoutFDLeak(t *testing.T) {
 	err  error
 	}
 	dials := listenerBacklog + 100
-	maxGoodConnect := listenerBacklog + 5 // empirically 131 good ones (of 128). who knows?
+	// used to be listenerBacklog + 5, but was found to be unreliable, issue 4384.
+	maxGoodConnect := 150
 	resc := make(chan connErr)
 	for i := 0; i < dials; i++ {
 		go func() {

コアとなるコードの解説

変更はsrc/pkg/net/dial_test.goファイル内のTestDialTimeoutFDLeak関数にあります。

元のコードでは、maxGoodConnect変数が以下のように定義されていました。

maxGoodConnect := listenerBacklog + 5 // empirically 131 good ones (of 128). who knows?

この行は、listenerBacklogという動的な値に依存してmaxGoodConnectを計算していました。コメントには「経験的に131の良い接続(128のうち)。誰にもわからない?」とあり、この値が経験的なものであり、その信頼性に疑問があることが示唆されています。

このコミットでは、この行が以下のように変更されました。

// used to be listenerBacklog + 5, but was found to be unreliable, issue 4384.
maxGoodConnect := 150

変更のポイントは以下の通りです。

  1. コメントの追加: listenerBacklog + 5が以前使用されていたが、Issue 4384で信頼性がないことが判明した、という説明が追加されました。これは、この変更の背景と理由を明確にしています。
  2. 固定値への変更: maxGoodConnectの値がlistenerBacklog + 5から150という固定値に変更されました。これにより、テストはオペレーティングシステムのlistenerBacklog設定に依存しなくなり、より予測可能で安定した挙動を示すようになります。150という値は、テストが意図するシナリオ(一部の接続が成功し、一部がタイムアウトする)を確実に再現できる十分な数として選ばれたと考えられます。

この変更により、TestDialTimeoutFDLeakテストは、環境による変動に左右されずに、ファイルディスクリプタリークの検出という本来の目的をより信頼性高く果たすことができるようになりました。

関連リンク

参考にした情報源リンク