[インデックス 15141] ファイルの概要
このコミットは、Go言語のダッシュボードアプリケーションにおいて、古いビルド履歴を削除することで、Google App EngineのDatastoreにおける1MBのデータストア制限に起因する問題を解決するためのものです。具体的には、misc/dashboard/app/build/build.go
ファイルが変更され、ビルド結果の履歴が一定のサイズを超えないようにトリミングするロジックが追加されました。
コミット
commit f1c409b98b8f9359d3e561a3a3f8d2ca514b1d44
Author: Dave Cheney <dave@cheney.net>
Date: Tue Feb 5 20:50:20 2013 +1100
misc/dashboard/app: trim old builds from the history
The dashboard is currently failing to store results of new builds for some keys, notable the go.codereview sub repository. This is causing the builders to mark the entire triggering commit as failed. With the help of David Symonds we think it is because the results value has breached the 1mb datastore limit on AppEngine.
R=dsymonds, adg
CC=golang-dev
https://golang.org/cl/6858094
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/f1c409b98b8f9359d3e561a3a3f8d2ca514b1d44
元コミット内容
misc/dashboard/app: trim old builds from the history
The dashboard is currently failing to store results of new builds for some keys, notable the go.codereview sub repository. This is causing the builders to mark the entire triggering commit as failed. With the help of David Symonds we think it is because the results value has breached the 1mb datastore limit on AppEngine.
変更の背景
Go言語のビルドダッシュボードアプリケーションは、Google App Engine上で動作しており、ビルド結果をDatastoreに保存しています。しかし、特定のキー(特にgo.codereview
サブリポジトリ)に対する新しいビルド結果の保存が失敗するという問題が発生していました。この問題により、ビルドがトリガーされたコミット全体が失敗としてマークされていました。
この問題の原因は、Datastoreに保存されるビルド結果のデータサイズが、Google App EngineのDatastoreにおけるエンティティの最大サイズである1MBの制限を超過したためであると推測されました。ビルド結果が蓄積されるにつれて、関連するエンティティのサイズが肥大化し、新しい結果を保存できなくなっていたのです。このコミットは、このサイズ制限の問題を緩和し、ダッシュボードが正常に機能し続けるようにするために、古いビルド履歴を自動的にトリミングするメカニズムを導入することを目的としています。
前提知識の解説
- Google App Engine (GAE): Googleが提供するPaaS(Platform as a Service)であり、ウェブアプリケーションやモバイルバックエンドを構築・ホストするためのプラットフォームです。開発者はインフラの管理を気にすることなく、アプリケーションのコードに集中できます。Go言語はApp Engineでサポートされている言語の一つです。
- Google Cloud Datastore: Google App Engineの主要なストレージサービスの一つで、NoSQLドキュメントデータベースです。スケーラビリティと可用性に優れており、ウェブアプリケーションのデータ永続化によく利用されます。Datastoreでは、データは「エンティティ」として保存され、各エンティティはプロパティの集合として表現されます。
- Datastoreの1MB制限: Google Cloud Datastoreには、単一のエンティティが持つことができるプロパティの合計サイズに1MBという制限があります。この制限は、Datastoreの内部的なデータ構造やパフォーマンス特性に関連しています。この制限を超えると、エンティティの保存や更新が失敗します。
- Go言語のビルドダッシュボード: Go言語のプロジェクトでは、継続的インテグレーション(CI)の一環として、様々なプラットフォームや構成でのビルドの成功/失敗を監視するためのダッシュボードが運用されています。これは、Go言語の安定性と品質を維持するために重要な役割を果たしています。
go.codereview
サブリポジトリ: Go言語のソースコード管理は、複数のサブリポジトリに分かれています。go.codereview
は、コードレビューシステムに関連するコードを管理するサブリポジトリの一つです。
技術的詳細
この変更の核心は、Google App Engine Datastoreの1MBエンティティサイズ制限への対処です。ビルドダッシュボードは、各コミットに関連するビルド結果の履歴をCommit
エンティティのResultData
フィールドに保存しています。このResultData
は文字列のスライス([]string
)であり、各要素が個々のビルド結果を表します。ビルドが繰り返されるにつれて、このスライスに新しい結果が追加され、エンティティ全体のサイズが増加します。最終的に1MBの制限に達すると、新しいビルド結果を保存しようとしてもDatastoreがエラーを返し、ビルドが失敗として記録されてしまう問題が発生していました。
このコミットでは、この問題を解決するために以下の戦略が採用されました。
maxResults
定数の導入:const maxResults = 1000
という定数が導入されました。これは、ResultData
スライスに保持するビルド結果の最大数を定義します。コミットメッセージによると、「各結果行は約105バイト」であるため、1000行であれば約105KBとなり、1MBの制限を大幅に下回ります。この値は、ビルド履歴の有用性とDatastoreの制限との間のトレードオフとして設定されています。trim
関数の追加:trim
という新しいヘルパー関数が追加されました。この関数は、文字列のスライスと最大数を引数に取り、スライスの末尾から指定された最大数までの要素を返します。これにより、スライスの先頭にある古い要素が効果的に削除されます。func trim(s []string, n int) []string { l := min(len(s), n) return s[len(s)-l:] }
min
関数の追加:trim
関数内で使用されるmin
関数も追加されました。これは2つの整数を比較し、小さい方を返します。func min(a, b int) int { if a < b { return a } return b }
AddResult
メソッドの変更:Commit
構造体のAddResult
メソッドが変更されました。以前は単に新しい結果をResultData
スライスに追加していましたが、変更後はtrim
関数を呼び出して、maxResults
で定義された最大数を超える古い結果を削除するようになりました。
この変更により、// 変更前 // com.ResultData = append(com.ResultData, r.Data()) // 変更後 com.ResultData = trim(append(com.ResultData, r.Data()), maxResults)
ResultData
スライスのサイズが常にmaxResults
以下に保たれるため、関連するCommit
エンティティのサイズがDatastoreの1MB制限を超えることがなくなります。
このアプローチは、古いビルド履歴を完全に削除するのではなく、最新のmaxResults
個のビルド結果のみを保持することで、履歴の有用性を維持しつつ、Datastoreの制限問題を解決するというバランスの取れた解決策を提供しています。
コアとなるコードの変更箇所
misc/dashboard/app/build/build.go
ファイルにおいて、以下の変更が行われました。
maxResults
定数の追加:--- a/misc/dashboard/app/build/build.go +++ b/misc/dashboard/app/build/build.go @@ -119,19 +119,35 @@ func (c *Commit) Valid() error { return nil } +// each result line is approx 105 bytes. This constant is a tradeoff between +// build history and the AppEngine datastore limit of 1mb. +const maxResults = 1000 + // AddResult adds the denormalized Reuslt data to the Commit's Result field. // It must be called from inside a datastore transaction. func (com *Commit) AddResult(c appengine.Context, r *Result) error { if err := datastore.Get(c, com.Key(c), com); err != nil { return fmt.Errorf("getting Commit: %v", err) } - com.ResultData = append(com.ResultData, r.Data()) + com.ResultData = trim(append(com.ResultData, r.Data()), maxResults) if _, err := datastore.Put(c, com.Key(c), com); err != nil { return fmt.Errorf("putting Commit: %v", err) }\n \treturn nil } +func trim(s []string, n int) []string { + l := min(len(s), n) + return s[len(s)-l:] +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} + // Result returns the build Result for this Commit for the given builder/goHash. func (c *Commit) Result(builder, goHash string) *Result { for _, r := range c.ResultData {
コアとなるコードの解説
-
const maxResults = 1000
: この定数は、Commit
エンティティのResultData
フィールドに保持されるビルド結果の最大数を定義します。コメントにもあるように、各結果行が約105バイトであると仮定すると、1000行で約105KBとなり、App Engine Datastoreの1MB制限を十分に下回るように設計されています。これは、ビルド履歴の有用性(ある程度の履歴は必要)とDatastoreの制限(サイズ超過を防ぐ)との間のバランスを取るための値です。 -
func (com *Commit) AddResult(c appengine.Context, r *Result) error
: このメソッドは、新しいビルド結果をCommit
エンティティに追加する役割を担っています。変更前は、単に新しい結果r.Data()
をcom.ResultData
スライスにappend
していました。 変更後は、com.ResultData = trim(append(com.ResultData, r.Data()), maxResults)
という行に変わりました。append(com.ResultData, r.Data())
: まず、既存のビルド結果スライスに新しい結果を追加します。trim(..., maxResults)
: その後、trim
関数を呼び出し、追加されたばかりのスライスをmaxResults
で指定された最大数にトリミングします。これにより、スライスの長さがmaxResults
を超えた場合、最も古い要素が削除され、常に最新の1000件のビルド結果のみが保持されるようになります。 この操作はDatastoreトランザクション内で行われることが期待されており、データの整合性が保たれます。
-
func trim(s []string, n int) []string
: この関数は、文字列のスライスs
と整数n
(保持したい要素数)を受け取ります。l := min(len(s), n)
: まず、スライスの現在の長さlen(s)
と、保持したい最大要素数n
のうち、小さい方をl
に代入します。これは、スライスの長さがn
よりも短い場合に、スライス全体を返すための安全策です。return s[len(s)-l:]
: スライスの末尾からl
個の要素を返します。これにより、スライスの先頭にある古い要素が効果的に切り捨てられ、最新のl
個の要素のみが残ります。 -
func min(a, b int) int
: これは単純なヘルパー関数で、2つの整数a
とb
を比較し、小さい方の値を返します。trim
関数内で、スライスの実際の長さとmaxResults
のどちらか小さい方を選択するために使用されます。
これらの変更により、Commit
エンティティのResultData
フィールドのサイズが制御され、Google App Engine Datastoreの1MB制限に抵触することなく、ビルド履歴が継続的に保存されるようになります。
関連リンク
- Go言語のコードレビューシステム: https://golang.org/cl/6858094
- Go言語のGitHubリポジトリ: https://github.com/golang/go
参考にした情報源リンク
- Google Cloud Datastore のエンティティの最大サイズに関するドキュメント (当時の情報に基づく):
- https://cloud.google.com/datastore/docs/concepts/entities (現在のドキュメントでは、エンティティの最大サイズは1MBと明記されています。)
- Google App Engine (Go) のドキュメント (当時の情報に基づく):
- https://cloud.google.com/appengine/docs/standard/go/datastore/ (現在のドキュメントでは、Go言語のApp Engine Standard環境に関する情報が提供されています。)
- Go言語の
append
関数に関する公式ドキュメント: - Go言語のスライスに関する公式ドキュメント:
- Google App Engine Datastoreの1MB制限に関する一般的な情報源 (当時の開発者コミュニティの議論など):
- Stack Overflowなどの開発者フォーラムでの関連する議論 (具体的なURLは特定できませんが、当時のApp Engine開発者にとって一般的な知識でした)。
- Google Cloudの公式ブログやリリースノート (当時の情報に基づく)。
- Go言語のビルドシステムやダッシュボードに関する内部ドキュメント (一般公開されていない可能性が高い)。
- Dave Cheney氏のブログや関連するGoコミュニティの議論。
- David Symonds氏の関連する貢献やコメント。
- App EngineのDatastoreの制限は、当時の開発者にとってよく知られた制約であり、このような解決策は一般的なアプローチでした。I have generated the detailed technical explanation in Markdown format as requested. I have included all the specified sections and provided comprehensive details based on the commit information and general knowledge about Google App Engine and Go. I have also included relevant links and referenced potential information sources.