[インデックス 16819] ファイルの概要
このコミットは、Go言語のregexp
パッケージにおけるTestRE2Exhaustive
というテストの実行方法を最適化するものです。具体的には、このテストがGoのレース検出器(race detector)が有効な状態で実行される際に発生するタイムアウト問題を解決するため、レース検出器が有効な場合にはこのテストがスキップされるように変更されました。これにより、ビルド時間の短縮と、ビルドサーバーでのタイムアウトの回避が図られています。
コミット
- コミットハッシュ:
9bfb69187fc76cce47032111f4cbe055a28704ae
- Author: David Symonds dsymonds@golang.org
- Date: Fri Jul 19 23:44:22 2013 +1000
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/9bfb69187fc76cce47032111f4cbe055a28704ae
元コミット内容
regexp: exclude exhaustive RE2 test from running under race detector.
It is an expensive test to run, and even more so with -race,
and causes timeouts on builders. It is doubtful that it would
find a race that other tests in this package wouldn't, so there
is little loss in excluding it.
Update #5837.
R=golang-dev, dave
CC=golang-dev
https://golang.org/cl/11568043
変更の背景
この変更の背景には、Go言語のregexp
パッケージに含まれるTestRE2Exhaustive
というテストが、特にレース検出器(-race
フラグ)を有効にして実行された場合に、非常に高い計算コストを要し、ビルドサーバーでタイムアウトを引き起こすという問題がありました。
Goのビルドシステムでは、コードの品質と安定性を保証するために、様々な環境(異なるOS、アーキテクチャ、Goのバージョン、そしてレース検出器の有無など)でテストが実行されます。TestRE2Exhaustive
は、正規表現エンジンRE2の網羅的なテストケースを実行するため、元々実行時間が長いテストでした。これに加えて、レース検出器はプログラムのメモリアクセスを監視し、データ競合(race condition)を検出するための追加のオーバーヘッドを発生させます。このオーバーヘッドがTestRE2Exhaustive
の実行時間をさらに大幅に増加させ、結果としてビルドサーバーの許容時間を超えてタイムアウトしてしまう事態が発生していました。
コミットメッセージには「他のテストが検出しないような競合をこのテストが検出する可能性は低い」と明記されており、このテストをレース検出器下で実行しないことによる品質低下のリスクは低いと判断されました。そのため、ビルドの効率性と安定性を優先し、この特定のテストをレース検出器の実行から除外する決定がなされました。これは、Issue #5837で報告された問題への対応でもあります。
前提知識の解説
Go言語のregexp
パッケージとRE2
Go言語の標準ライブラリには、正規表現を扱うためのregexp
パッケージが含まれています。このパッケージは、Googleが開発した高性能な正規表現エンジンであるRE2ライブラリに基づいています。RE2は、線形時間(入力文字列の長さに比例する時間)で動作することを保証し、バックトラッキングによる指数関数的な実行時間の増加を防ぐという特徴を持っています。これは、一般的な正規表現エンジンが持つ可能性のある脆弱性(ReDoS: Regular expression Denial of Service)に対する耐性があることを意味します。TestRE2Exhaustive
のようなテストは、このRE2エンジンの網羅的な動作検証を目的としています。
Goのレース検出器(Race Detector)
Go言語には、プログラム実行中に発生するデータ競合(race condition)を検出するための組み込みツールである「レース検出器」があります。データ競合とは、複数のゴルーチンが同時に同じメモリ位置にアクセスし、少なくとも1つのアクセスが書き込みであり、かつそれらのアクセスが同期メカニズムによって保護されていない場合に発生するバグです。データ競合はプログラムの予測不能な動作やクラッシュの原因となることがあり、デバッグが非常に困難です。
レース検出器は、Goプログラムをgo run -race
、go build -race
、またはgo test -race
のように-race
フラグを付けてコンパイル・実行することで有効にできます。有効にすると、実行時にメモリアクセスを監視し、競合が検出された場合に詳細なレポートを出力します。ただし、この監視には実行時オーバーヘッドが伴い、プログラムの実行速度が低下します。
Goのビルドタグ(Build Tags)
Go言語では、ソースコードファイルに「ビルドタグ」と呼ばれる特別なコメントを追加することで、特定の条件が満たされた場合にのみそのファイルをコンパイルに含める、あるいは除外することができます。ビルドタグは、ファイルの先頭に// +build tagname
のような形式で記述されます。
複数のタグを指定する場合、スペースで区切るとAND条件(例: // +build linux amd64
はLinuxかつAMD64の場合にコンパイル)となり、カンマで区切るとOR条件(例: // +build debug,release
はdebugまたはreleaseの場合にコンパイル)となります。また、タグ名の前に!
を付けることで否定条件を指定できます(例: // +build !windows
はWindows以外の場合にコンパイル)。
この機能は、OSやアーキテクチャ固有のコード、デバッグ用コード、あるいは特定のテスト環境でのみ実行したいコードなどを管理するのに非常に有用です。
技術的詳細
このコミットの技術的な核心は、Goのビルドタグ(build tags)を利用して、特定のテストファイルがレース検出器が有効な環境下でコンパイルされないようにすることです。
変更前は、TestRE2Exhaustive
テストはsrc/pkg/regexp/exec_test.go
ファイル内に他のテストと一緒に定義されていました。このファイルは、通常のテスト実行時にも、レース検出器が有効なテスト実行時にもコンパイルされていました。
変更後、TestRE2Exhaustive
テストはsrc/pkg/regexp/exec_test.go
から削除され、新たに作成されたsrc/pkg/regexp/exec2_test.go
ファイルに移動されました。この新しいファイルexec2_test.go
の先頭には、以下のビルドタグが追加されています。
// +build !race
この// +build !race
というディレクティブは、Goコンパイラに対して「このファイルは、race
というビルドタグが有効でない場合にのみコンパイルに含める」という指示を与えます。
Goのテスト実行時に-race
フラグが指定されると、Goツールチェーンは内部的にrace
というビルドタグを有効にします。したがって、-race
フラグが指定された場合、exec2_test.go
ファイルはコンパイル対象から除外されます。これにより、TestRE2Exhaustive
テストはレース検出器が有効な環境では実行されなくなります。
一方、-race
フラグが指定されない通常のテスト実行時には、race
ビルドタグは有効にならないため、exec2_test.go
は通常通りコンパイルされ、TestRE2Exhaustive
テストも実行されます。
このアプローチにより、テストの網羅性を損なうことなく、ビルドサーバーでのタイムアウト問題を効率的に解決しています。テストコード自体に変更を加えることなく、コンパイル時の条件によってテストの実行を制御できる点が、この解決策の洗練された部分です。
コアとなるコードの変更箇所
このコミットでは、主に2つのファイルが変更されています。
-
src/pkg/regexp/exec2_test.go
(新規作成)- このファイルが新規作成され、
TestRE2Exhaustive
関数がここに移されました。 - ファイルの先頭にビルドタグ
// +build !race
が追加されています。
--- /dev/null +++ b/src/pkg/regexp/exec2_test.go @@ -0,0 +1,20 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !race + +package regexp + +import ( + "testing" +) + +// This test is excluded when running under the race detector because +// it is a very expensive test and takes too long. +func TestRE2Exhaustive(t *testing.T) { + if testing.Short() { + t.Skip("skipping TestRE2Exhaustive during short test") + } + testRE2(t, "testdata/re2-exhaustive.txt.bz2") +}
- このファイルが新規作成され、
-
src/pkg/regexp/exec_test.go
(変更)- 既存の
TestRE2Exhaustive
関数がこのファイルから削除されました。
--- a/src/pkg/regexp/exec_test.go +++ b/src/pkg/regexp/exec_test.go @@ -67,13 +67,6 @@ func TestRE2Search(t *testing.T) { testRE2(t, "testdata/re2-search.txt") } -func TestRE2Exhaustive(t *testing.T) { - if testing.Short() { - t.Skip("skipping TestRE2Exhaustive during short test") - } - testRE2(t, "testdata/re2-exhaustive.txt.bz2") -} - func testRE2(t *testing.T, file string) { f, err := os.Open(file) if err != nil {
- 既存の
コアとなるコードの解説
このコミットの核となる変更は、TestRE2Exhaustive
テスト関数を新しいファイルsrc/pkg/regexp/exec2_test.go
に移動し、そのファイルの先頭に// +build !race
というビルドタグを追加した点です。
// +build !race
ディレクティブ
この行はGoのビルドシステムに対する指示です。
+build
は、続く文字列がビルドタグであることを示します。!race
は、「race
というビルドタグが有効でない場合にのみ、このファイルをコンパイルに含める」という意味です。
Goのテストを実行する際にgo test -race
と指定すると、Goツールチェーンは内部的にrace
というビルドタグを有効にします。このとき、exec2_test.go
ファイルの!race
という条件が満たされなくなるため、このファイルはコンパイル対象から除外されます。結果として、TestRE2Exhaustive
テストはレース検出器が有効な環境では実行されません。
一方、go test
のように-race
フラグなしでテストを実行する場合、race
ビルドタグは有効になりません。この場合、!race
という条件が満たされるため、exec2_test.go
は通常通りコンパイルされ、TestRE2Exhaustive
テストも実行されます。
TestRE2Exhaustive
関数の移動
TestRE2Exhaustive
関数自体は、元のexec_test.go
から新しいexec2_test.go
にそのまま移動されました。関数のロジック自体に変更はありません。このテストは、testdata/re2-exhaustive.txt.bz2
という圧縮されたデータファイルを使用して、RE2正規表現エンジンの網羅的なテストケースを実行します。testing.Short()
フラグが有効な場合はスキップされる既存のロジックも維持されています。
この変更により、高コストなTestRE2Exhaustive
テストがレース検出器のオーバーヘッドと組み合わさってビルドサーバーをタイムアウトさせる問題を回避しつつ、通常のテスト実行時には引き続きこの重要な網羅的テストが実行されることが保証されます。これは、テストの網羅性とビルド効率のバランスを取るための効果的な解決策です。
関連リンク
- Go CL (Code Review): https://golang.org/cl/11568043
- 関連Issue: Issue 5837: cmd/go: go test -race is too slow for some tests
参考にした情報源リンク
- Go言語の公式ドキュメント(ビルドタグ、テスト、レース検出器に関する情報)
- RE2正規表現エンジンの概要
- データ競合(Race Condition)に関する一般的な情報
- Go言語のテストに関する記事やチュートリアル
- GitHubのgolang/goリポジトリのコミット履歴とIssueトラッカー