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

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

このコミットは、Go言語の標準ライブラリである net/http パッケージ内のベンチマークテスト BenchmarkServerFakeConnNoKeepAlive の不安定性(flakiness)を解消するための修正です。具体的には、テスト用のコネクション (testConn) が使用するチャネルのバッファリングに関する問題に対処しています。

コミット

commit 3add0fef1e7e398e73bd4a7bdcb278bef5683395
Author: Rémy Oudompheng <oudomphe@phare.normalesup.org>
Date:   Mon Mar 25 08:17:10 2013 +0100

    net/http: deflake BenchmarkServerFakeConnNoKeepAlive.
    
    Fixes #5121.
    
    R=golang-dev, bradfitz
    CC=golang-dev
    https://golang.org/cl/7814046

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

https://github.com/golang/go/commit/3add0fef1e7e398e73bd4a7bdcb278bef5683395

元コミット内容

このコミットは、net/http パッケージの serve_test.go ファイルに対して行われた変更です。BenchmarkServerFakeConnNoKeepAlive ベンチマークが時折失敗する(flakyである)問題を解決することを目的としています。この問題は、GoのIssue #5121 で報告されたものです。修正内容は、テスト用のコネクション構造体 testConn 内の closec チャネルの定義を、バッファなしチャネルからバッファ付きチャネル(バッファサイズ1)に変更することです。これにより、testConn.Close メソッドがチャネルに書き込む際にブロックされる可能性を排除し、ベンチマークの信頼性を向上させています。

変更の背景

ソフトウェア開発において、テストの信頼性は非常に重要です。特にベンチマークテストは、コードのパフォーマンス特性を測定するために用いられますが、その結果が不安定である(flakyである)と、開発者はその結果を信頼できなくなり、パフォーマンスの回帰や改善を見落とす可能性があります。

このコミットの背景には、net/http パッケージの BenchmarkServerFakeConnNoKeepAlive というベンチマークが、特定の条件下で不安定な挙動を示していたという問題があります。ベンチマークが不安定であると、CI/CDパイプラインでの自動テストがランダムに失敗し、開発者の生産性を低下させます。コミットメッセージにある Fixes #5121 は、この問題がGoのIssueトラッカーで報告され、その解決が求められていたことを示しています。

具体的な不安定性の原因は、テスト用のコネクション (testConn) がクローズされたことを通知するために使用するGoのチャネルの設計にありました。このチャネルがバッファなしで作成されていたため、testConn.Close メソッドがチャネルに値を送信しようとした際に、受信側が準備できていないと送信がブロックされ、ベンチマークの実行がハングアップしたり、タイムアウトしたりする原因となっていました。このような状況は、ベンチマークの実行タイミングやシステムリソースの状況によって発生したりしなかったりするため、「flaky」な挙動として現れます。

前提知識の解説

  • net/http パッケージ: Go言語の標準ライブラリで、HTTPクライアントとサーバーの実装を提供します。ウェブアプリケーションやAPIサーバーを構築する際に中心的に使用されます。
  • ベンチマークテスト (Benchmark Test): Go言語のテストフレームワークの一部で、コードのパフォーマンスを測定するために使用されます。go test -bench=. コマンドで実行され、関数の実行時間やメモリ使用量などを計測します。ベンチマーク関数は BenchmarkXxx という命名規則に従います。
  • testConn: net/http パッケージのテストコード内で使用される、HTTPコネクションをシミュレートするためのカスタム構造体です。実際のネットワークI/Oを行わず、テストの制御を容易にするために使用されます。
  • Keep-Alive: HTTP/1.1の機能で、単一のTCPコネクション上で複数のHTTPリクエスト/レスポンスをやり取りすることを可能にします。これにより、コネクションの確立と切断のオーバーヘッドを削減し、パフォーマンスを向上させます。BenchmarkServerFakeConnNoKeepAlive は、このKeep-Alive機能を使用しない場合のサーバーのパフォーマンスを測定することを意図しています。
  • Flaky Test (不安定なテスト): 同じコードベースに対して、変更がないにもかかわらず、テストが成功したり失敗したりするテストのことです。並行処理の競合、外部リソースの可用性、タイムアウト、環境依存性などが原因で発生することが多いです。Flakyテストは開発者の信頼を損ない、CI/CDパイプラインの効率を低下させます。
  • Goのチャネル (Channels): Go言語におけるゴルーチン間の通信メカニズムです。チャネルを通じて値を送受信することで、安全な並行処理を実現します。
    • バッファなしチャネル (Unbuffered Channel): make(chan Type) のように作成され、送信操作は受信操作が準備できるまでブロックされ、受信操作は送信操作が準備できるまでブロックされます。同期的な通信に使用されます。
    • バッファ付きチャネル (Buffered Channel): make(chan Type, capacity) のように作成され、指定された容量まで値を格納できます。チャネルが満杯でない限り、送信操作はブロックされず、チャネルが空でない限り、受信操作はブロックされません。非同期的な通信や、送信側と受信側の速度差を吸収するために使用されます。

技術的詳細

このコミットの技術的な核心は、Goのチャネルのバッファリングの挙動にあります。

net/http/serve_test.go 内の testConn 構造体は、テスト中にコネクションがクローズされたことをシミュレートするために closec というチャネルを持っていました。

変更前:

type testConn struct {
    // ...
    closec chan bool // コネクションがクローズされたことを通知するチャネル
    // ...
}

この closecmake(chan bool) で作成されており、これはバッファなしチャネルです。バッファなしチャネルへの送信操作は、その値を読み取る受信操作が準備できるまでブロックされます。

testConn.Close() メソッドは、この closec チャネルに true を送信することで、コネクションがクローズされたことを通知します。

func (c *testConn) Close() error {
    // ...
    c.closec <- true // ここで送信
    // ...
    return nil
}

BenchmarkServerFakeConnNoKeepAlive のようなベンチマークでは、多数のHTTPリクエストが高速に処理され、それに伴い多数の testConn インスタンスが生成され、クローズされます。問題は、testConn.Close() が呼び出された際に、closec から値を受け取るゴルーチンが常に即座に準備できているとは限らない点にありました。

もし closec から値を受け取るゴルーチンがまだ実行されていないか、他の処理でビジーである場合、c.closec <- true の送信操作はブロックされます。ベンチマークの性質上、このようなブロックが頻繁に発生すると、ベンチマークの実行時間が不必要に長くなったり、最悪の場合、デッドロックやタイムアウトを引き起こしてテストが失敗する原因となります。これが「flaky」な挙動の根本原因でした。

この修正では、closec チャネルをバッファ付きチャネルに変更しています。

変更後:

type testConn struct {
    // ...
    closec chan bool // コネクションがクローズされたことを通知するチャネル
    // ...
}

// testConnの初期化部分
conn := &testConn{
    // testConn.Close will not push into the channel
    // if it's full.
    closec: make(chan bool, 1), // バッファサイズ1のチャネルに変更
}

make(chan bool, 1) は、1つの要素を格納できるバッファ付きチャネルを作成します。これにより、testConn.Close()c.closec <- true を実行する際に、受信側がまだ準備できていなくても、チャネルのバッファに空きがあれば(この場合は常に1つの空きがある)、送信操作はブロックされずに即座に完了します。これにより、ベンチマークの実行が不必要にブロックされることがなくなり、不安定性が解消されます。

コメント // testConn.Close will not push into the channel // if it's full. は、この変更の意図を明確にしています。つまり、Close メソッドはチャネルが満杯でない限りブロックされない、という保証を与えています。このベンチマークの文脈では、closec は一度しか使用されないため、バッファサイズ1で十分であり、常にブロックされないことが保証されます。

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

--- a/src/pkg/net/http/serve_test.go
+++ b/src/pkg/net/http/serve_test.go
@@ -1635,7 +1635,9 @@ Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
 	res := []byte("Hello world!\n")
 
 	conn := &testConn{
-		closec: make(chan bool),
+		// testConn.Close will not push into the channel
+		// if it's full.
+		closec: make(chan bool, 1),
 	}
 	handler := HandlerFunc(func(rw ResponseWriter, r *Request) {
 		rw.Header().Set("Content-Type", "text/html; charset=utf-8")

コアとなるコードの解説

変更は src/pkg/net/http/serve_test.go ファイルの BenchmarkServerFakeConnNoKeepAlive 関数内で行われています。

具体的には、testConn 構造体のインスタンスを初期化する部分で、closec フィールドの初期化方法が変更されています。

  • 変更前: closec: make(chan bool), これはバッファなしチャネルを作成します。このチャネルへの送信は、受信側が準備できるまでブロックされます。ベンチマークの高速な実行環境では、このブロックが原因でテストが不安定になる可能性がありました。

  • 変更後: closec: make(chan bool, 1), これはバッファサイズが1のバッファ付きチャネルを作成します。これにより、testConn.Close() メソッドが closec に値を送信する際に、受信側がまだ準備できていなくても、チャネルのバッファに1つの要素を格納できるため、送信操作はブロックされずに即座に完了します。これにより、ベンチマークの実行フローがスムーズになり、不安定性が解消されます。

追加されたコメント // testConn.Close will not push into the channel // if it's full. は、この変更の意図、すなわち Close メソッドがチャネルへの送信でブロックされないようにするという目的を明確に示しています。

関連リンク

  • GitHubコミットページ: https://github.com/golang/go/commit/3add0fef1e7e398e73bd4a7bdcb278bef5683395
  • Go Issue #5121: コミットメッセージには Fixes #5121 と記載されていますが、現在のGoのIssueトラッカーでこの番号のIssueを直接特定することは困難でした。古いIssue番号は再編成されたり、別のIssueに統合されたりすることがあります。しかし、同様のベンチマークの不安定性に関する問題として、Go Issue 22540: net/http: spurious 'connection reset by peer' under load が存在し、BenchmarkServerFakeConnNoKeepAlive の不安定性に関連する議論が含まれています。このコミットが直接 #5121 を修正したものであることはコミットメッセージから明らかですが、公開されているIssueの履歴からは直接的な関連を見つけることができませんでした。
  • Go CL (Change List) リンク: コミットメッセージに記載されている https://golang.org/cl/7814046 は、GoのChange Listへのリンクとして提供されていますが、Web検索の結果、この番号はGoのCL番号としては認識されず、IMO(国際海事機関)の船舶識別番号として認識されることが判明しました。これは、コミットが作成された当時の内部的なCLシステムが現在とは異なるか、リンクが誤って記載された可能性を示唆しています。

参考にした情報源リンク

  • Go言語のチャネルに関する公式ドキュメントやチュートリアル (Go言語の公式ドキュメント)
  • Go言語のベンチマークテストに関する公式ドキュメント (Go言語の公式ドキュメント)
  • Go Issue 22540: net/http: spurious 'connection reset by peer' under load (GitHub)
  • Web検索結果: "Go issue 5121 net/http BenchmarkServerFakeConnNoKeepAlive deflake"
  • Web検索結果: "golang.org/cl/7814046" (IMO番号に関する情報)