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

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

このコミットは、Goプロジェクトのダッシュボードおよびビルドシステムに関連する変更を導入しています。具体的には、ビルドおよびテストの実行時間を記録し、ダッシュボードに報告する機能を追加しています。これにより、Goのビルドプロセスのパフォーマンスをより詳細に追跡・分析できるようになります。

コミット

このコミットは、Goプロジェクトのビルドシステムとダッシュボードの連携を強化し、ビルドおよびテストの実行時間を記録・報告する機能を追加します。これにより、ビルドプロセスの効率性を監視し、潜在的なパフォーマンスボトルネックを特定するための重要なメトリクスが提供されます。

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

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

元コミット内容

builder: report run time
dashboard: record run time

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5504054

変更の背景

Goプロジェクトでは、継続的なインテグレーション(CI)とビルドプロセスの健全性を監視するために、ダッシュボードシステムが運用されています。これまでのシステムでは、ビルドの成功/失敗やログの記録は行われていましたが、個々のビルドやテストにかかる具体的な実行時間に関するメトリクスは不足していました。

この不足は、ビルド時間の変動を追跡したり、特定の変更がビルドパフォーマンスに与える影響を評価したりする上で課題となっていました。例えば、新しい機能が追加された際にビルド時間が大幅に増加した場合、その原因を特定するためには、実行時間の詳細なデータが必要となります。

このコミットは、このような背景から、ビルドおよびテストの実行時間をダッシュボードに報告し、記録する機能を追加することで、GoプロジェクトのCI/CDパイプラインの可視性と分析能力を向上させることを目的としています。これにより、開発者はビルドパフォーマンスの傾向を把握し、最適化の機会を特定できるようになります。

前提知識の解説

このコミットを理解するためには、以下の概念について基本的な知識があると役立ちます。

Goプロジェクトのビルドシステムとダッシュボード

Goプロジェクトは、その開発プロセスにおいて、独自のビルドシステムとダッシュボードを運用しています。

  • Go Builder: Goのソースコードをビルドし、テストを実行する役割を担う自動化されたシステムです。様々なプラットフォームやアーキテクチャ向けにGoをビルドし、その結果をダッシュボードに報告します。
  • Go Dashboard: Go Builderから送られてくるビルド結果を集約し、ウェブインターフェースを通じて表示するシステムです。ビルドの成功/失敗、テスト結果、ビルドログなどを一元的に管理し、開発者がプロジェクトの健全性を一目で確認できるようにします。このダッシュボードは、Google App Engine上で動作していることが示唆されています。

Google App Engine (GAE)

Google App Engineは、Googleが提供するPlatform as a Service (PaaS) です。ウェブアプリケーションやモバイルバックエンドを構築・デプロイするためのフルマネージド環境を提供します。開発者はインフラの管理を気にすることなく、アプリケーションのコードに集中できます。

GAEの主要なコンポーネントとして、以下のものが挙げられます。

  • Datastore: Google Cloud Datastoreは、GAEアプリケーションが利用できるNoSQLドキュメントデータベースです。スケーラブルで可用性の高いデータストレージを提供し、アプリケーションのデータを永続化するために使用されます。このコミットでは、ビルド結果(Result構造体)をDatastoreに保存するために利用されています。

Go言語のtime.Duration

Go言語のtimeパッケージには、時間の長さを表すDuration型が定義されています。これはint64のエイリアスであり、ナノ秒単位で時間を表現します。時間の計算や計測に非常に便利で、このコミットではビルドの実行時間を計測するために使用されています。

継続的インテグレーション (CI)

継続的インテグレーション(CI)は、ソフトウェア開発の手法の一つで、開発者がコードの変更を頻繁に共有リポジトリにマージし、自動化されたビルドとテストを継続的に実行することで、ソフトウェアの品質を維持し、統合の問題を早期に発見することを目的とします。Goのダッシュボードとビルドシステムは、このCIプロセスを支える重要なインフラです。

技術的詳細

このコミットは、Goのビルドシステムとダッシュボード間のデータ交換に、ビルド実行時間という新しいメトリクスを追加します。

  1. misc/dashboard/app/build/build.go の変更:

    • Result構造体にRunTime int64フィールドが追加されました。このフィールドは、ビルドとテストにかかった時間をナノ秒単位で格納することを意図しています。
    • datastore:",noindex"タグが付けられていないため、デフォルトでDatastoreのインデックスが作成される可能性があります。これは、RunTimeに基づいて結果をクエリする際に役立ちます。
    type Result struct {
        OK      bool
        Log     string `datastore:"-"`        // for JSON unmarshaling only
        LogHash string `datastore:",noindex"` // Key to the Log record.
        RunTime int64 // time to build+test in nanoseconds
    }
    
  2. misc/dashboard/builder/http.go の変更:

    • recordResult関数のシグネチャが変更され、runTime time.Durationという新しい引数が追加されました。これにより、ビルド結果をダッシュボードに送信する際に、実行時間も一緒に渡せるようになります。
    • ダッシュボードに送信されるJSONオブジェクト(req)に、"RunTime": runTime,というキーと値のペアが追加されました。これは、ビルド実行時間がHTTPリクエストのペイロードに含まれることを意味します。
    // recordResult sends build results to the dashboard
    func (b *Builder) recordResult(ok bool, pkg, hash, goHash, buildLog string, runTime time.Duration) error {
        req := obj{
            "Builder":     b.name,
            "PackagePath": pkg,
            "GoHash":      goHash,
            "OK":          ok,
            "Log":         buildLog,
            "RunTime":     runTime, // New field added here
        }
        // ...
    }
    
  3. misc/dashboard/builder/main.go の変更:

    • ビルドプロセスの開始直前にstartTime := time.Now()が追加され、現在の時刻が記録されます。
    • ビルドプロセスの完了直後にrunTime := time.Now().Sub(startTime)が追加され、startTimeからの経過時間がtime.Duration型で計算されます。
    • b.recordResult関数の呼び出し箇所がすべて更新され、計算されたrunTimeが新しい引数として渡されるようになりました。これにより、ビルドの実行時間がダッシュボードに正確に報告されます。
    • ただし、buildPackages関数内のrecordResult呼び出しでは、runTime0が渡されています。これは、パッケージごとのビルド時間ではなく、全体のビルド時間を追跡することに主眼が置かれているため、個々のパッケージビルドの実行時間はここでは重要ではない、あるいは別の方法で集計されることを示唆しています。
    func (b *Builder) buildHash(hash string) (err error) {
        // ...
        // build
        logfile := path.Join(workpath, "build.log")
        startTime := time.Now() // Start time measurement
        buildLog, status, err := runLog(b.envv(), logfile, srcDir, *buildCmd)
        runTime := time.Now().Sub(startTime) // Calculate run time
        if err != nil {
            return fmt.Errorf("%s: %s", *buildCmd, err)
        }
        // ...
        if status != 0 {
            // record failure
            return b.recordResult(false, "", hash, "", buildLog, runTime) // Pass runTime
        }
    
        // record success
        if err = b.recordResult(true, "", hash, "", "", runTime); err != nil { // Pass runTime
            return fmt.Errorf("recordResult: %s", err)
        }
        // ...
    }
    
    func (b *Builder) buildPackages(goRoot, goHash string) {
        // ...
        // record the result
        err = b.recordResult(ok, pkg, hash, goHash, buildLog, 0) // Pass 0 for runTime
        if err != nil {
            log.Printf("buildPackages %s: %v", pkg, err)
        }
        // ...
    }
    

これらの変更により、Goのビルドシステムは、ビルドの成功/失敗だけでなく、その実行時間もダッシュボードに報告するようになり、パフォーマンス監視の粒度が向上しました。

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

misc/dashboard/app/build/build.go

--- a/misc/dashboard/app/build/build.go
+++ b/misc/dashboard/app/build/build.go
@@ -180,6 +180,8 @@ type Result struct {
 	OK      bool
 	Log     string `datastore:"-"`        // for JSON unmarshaling only
 	LogHash string `datastore:",noindex"` // Key to the Log record.
+\n+\tRunTime int64 // time to build+test in nanoseconds
 }\n \n func (r *Result) Key(c appengine.Context) *datastore.Key {

misc/dashboard/builder/http.go

--- a/misc/dashboard/builder/http.go
+++ b/misc/dashboard/builder/http.go
@@ -111,7 +111,7 @@ func (b *Builder) todo(kind, pkg, goHash string) (rev string, err error) {
 }\n \n // recordResult sends build results to the dashboard
-func (b *Builder) recordResult(ok bool, pkg, hash, goHash, buildLog string) error {
+func (b *Builder) recordResult(ok bool, pkg, hash, goHash, buildLog string, runTime time.Duration) error {
 	req := obj{\n \t\t\"Builder\":     b.name,\n \t\t\"PackagePath\": pkg,\n@@ -119,6 +119,7 @@ func (b *Builder) recordResult(ok bool, pkg, hash, goHash, buildLog string) error {
 \t\t\"GoHash\":      goHash,\n \t\t\"OK\":          ok,\n \t\t\"Log\":         buildLog,\n+\t\t\"RunTime\":     runTime,\n \t}\n \targs := url.Values{\"key\": {b.key}, \"builder\": {b.name}}\n \treturn dash(\"POST\", \"result\", args, req, nil)\

misc/dashboard/builder/main.go

--- a/misc/dashboard/builder/main.go
+++ b/misc/dashboard/builder/main.go
@@ -294,7 +294,9 @@ func (b *Builder) buildHash(hash string) (err error) {
 \n \t// build\n \tlogfile := path.Join(workpath, "build.log")\n+\tstartTime := time.Now()\n \tbuildLog, status, err := runLog(b.envv(), logfile, srcDir, *buildCmd)\n+\trunTime := time.Now().Sub(startTime)\n \tif err != nil {\n \t\treturn fmt.Errorf("%s: %s", *buildCmd, err)\n \t}\n@@ -309,11 +311,11 @@ func (b *Builder) buildHash(hash string) (err error) {
 \n \tif status != 0 {\n \t\t// record failure\n-\t\treturn b.recordResult(false, "", hash, "", buildLog)\n+\t\treturn b.recordResult(false, "", hash, "", buildLog, runTime)\n \t}\n \n \t// record success\n-\tif err = b.recordResult(true, "", hash, "", ""); err != nil {\n+\tif err = b.recordResult(true, "", hash, "", "", runTime); err != nil {\n \t\treturn fmt.Errorf("recordResult: %s", err)\n \t}\n \n@@ -378,7 +380,7 @@ func (b *Builder) buildPackages(goRoot, goHash string) {
 \t\t}\n \n \t\t// record the result\n-\t\terr = b.recordResult(ok, pkg, hash, goHash, buildLog)\n+\t\terr = b.recordResult(ok, pkg, hash, goHash, buildLog, 0)\n \t\tif err != nil {\n \t\t\tlog.Printf("buildPackages %s: %v", pkg, err)\n \t\t}\n```

## コアとなるコードの解説

### `misc/dashboard/app/build/build.go`

*   **`Result` 構造体への `RunTime` フィールド追加**:
    `Result` 構造体は、Goダッシュボードがビルド結果をDatastoreに保存する際のデータモデルを定義しています。`RunTime int64` フィールドが追加されたことで、各ビルド結果に関連付けられた実行時間(ナノ秒単位)を永続的に保存できるようになりました。これにより、ダッシュボードはビルド時間の履歴を保持し、トレンド分析やパフォーマンス監視に利用できます。

### `misc/dashboard/builder/http.go`

*   **`recordResult` 関数のシグネチャ変更**:
    `recordResult` 関数は、Go Builderがビルド結果をGoダッシュボードにHTTP経由で送信する際に使用されます。この関数のシグネチャに `runTime time.Duration` 引数が追加されたことで、Builderはビルドの実行時間をダッシュボードに明示的に渡せるようになりました。
*   **HTTPリクエストペイロードへの `RunTime` の追加**:
    `recordResult` 関数内で構築されるHTTPリクエストのペイロード(`req` オブジェクト)に、`"RunTime": runTime,` が追加されました。これにより、ダッシュボードはHTTPリクエストボディから `RunTime` の値を受け取り、それを `Result` 構造体の `RunTime` フィールドにマッピングしてDatastoreに保存できるようになります。

### `misc/dashboard/builder/main.go`

*   **ビルド実行時間の計測ロジックの追加**:
    `buildHash` 関数内で、ビルドコマンド (`runLog`) の実行前後に `time.Now()` を呼び出すことで、ビルドの開始時刻と終了時刻を記録しています。`time.Now().Sub(startTime)` を使用して、これらの時刻間の差分を `time.Duration` 型の `runTime` として計算しています。これにより、ビルドの正確な実行時間を計測できるようになりました。
*   **`recordResult` 呼び出しへの `runTime` の引き渡し**:
    `buildHash` 関数内の `recordResult` の呼び出し箇所が修正され、計算された `runTime` が引数として渡されるようになりました。これにより、ビルドの成功/失敗に関わらず、その実行時間がダッシュボードに報告されます。
*   **`buildPackages` 関数での `runTime` の扱い**:
    `buildPackages` 関数内の `recordResult` 呼び出しでは、`runTime` に `0` が渡されています。これは、`buildPackages` が個々のパッケージのビルドを処理する際に、全体のビルド時間ではなく、より高レベルの `buildHash` 関数で計測される全体のビルド時間を重視しているためと考えられます。個々のパッケージのビルド時間は、このコンテキストでは詳細な追跡の対象外であるか、あるいは別の集計方法が想定されている可能性があります。

これらの変更は、Goのビルドシステムがビルドの実行時間を計測し、そのデータをダッシュボードに送信するエンドツーエンドのフローを確立しています。

## 関連リンク

*   Go プロジェクトの公式ウェブサイト: [https://golang.org/](https://golang.org/)
*   Google App Engine 公式ドキュメント: [https://cloud.google.com/appengine/docs](https://cloud.google.com/appengine/docs)
*   Go `time` パッケージのドキュメント: [https://pkg.go.dev/time](https://pkg.go.dev/time)

## 参考にした情報源リンク

*   Go プロジェクトのソースコード (GitHub): [https://github.com/golang/go](https://github.com/golang/go)
*   Google Cloud Datastore ドキュメント: [https://cloud.google.com/datastore/docs](https://cloud.google.com/datastore/docs)
*   継続的インテグレーション (CI) に関する一般的な情報源 (例: Wikipedia, Martin Fowler の記事など)
*   Go言語の`time.Duration`型に関する情報源 (例: Go言語の公式ドキュメント、Go言語のチュートリアルなど)
*   Goのダッシュボードとビルダーに関する具体的な実装の詳細については、Goプロジェクトのソースコード自体が最も信頼できる情報源となります。