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

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

このコミットは、Go言語プロジェクトのコードレビュープロセスを支援するための新しいApp Engineアプリケーション「gocodereview」を導入します。このアプリケーションは、既存のコードレビューシステム(Rietveldベース)から変更リスト(CL: Change List)の情報を取得し、レビュー担当者への割り当て、CLのステータス表示、古いCLのガベージコレクションなどの機能を提供するダッシュボードとして機能します。

コミット

misc/dashboard/codereview: new app.

This is live at http://gocodereview.appspot.com/.

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/6134043

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

https://github.com/golang/go/commit/0175e3f1e0a1604f2e3c7bd8b67c42d066f36fa1

元コミット内容

commit 0175e3f1e0a1604f2e3c7bd8b67c42d066f36fa1
Author: David Symonds <dsymonds@golang.org>
Date:   Fri Apr 27 16:36:02 2012 +1000

    misc/dashboard/codereview: new app.
    
    This is live at http://gocodereview.appspot.com/.
    
    R=golang-dev, r
    CC=golang-dev
    https://golang.org/cl/6134043

変更の背景

Go言語プロジェクトでは、コードの品質と整合性を保つために厳格なコードレビュープロセスが採用されています。当時、Googleが開発したRietveldというWebベースのコードレビューツールが広く使われていました。しかし、Rietveldは汎用的なツールであり、Goプロジェクト特有のニーズ(例えば、特定のレビュー担当者への割り当てや、多数のCLの効率的な管理)に完全に最適化されているわけではありませんでした。

このコミットは、Goプロジェクトのコントリビューターやレビュー担当者が、より効率的にコードレビューの状況を把握し、管理できるようにするための専用ダッシュボードアプリケーションを導入することを目的としています。これにより、レビュープロセスの透明性が向上し、ボトルネックの特定や、レビュー担当者の負荷分散が容易になることが期待されます。特に、http://gocodereview.appspot.com/ でライブ稼働していることから、開発チームの生産性向上に直結するツールとして位置づけられていたことが伺えます。

前提知識の解説

このコミットを理解するためには、以下の技術的背景知識が役立ちます。

  • Google App Engine (GAE): Googleが提供するPaaS(Platform as a Service)であり、Webアプリケーションやモバイルバックエンドを構築・ホストするためのプラットフォームです。Go言語を含む複数の言語をサポートしており、スケーラビリティ、信頼性、運用管理の容易さが特徴です。このアプリケーションはGAE上で動作するように設計されています。
    • app.yaml: App Engineアプリケーションの設定ファイルで、ランタイム、バージョン、URLルーティング、サービス(メール、タスクキューなど)の有効化などを定義します。
    • cron.yaml: App EngineのCronサービス設定ファイルで、定期的に実行されるジョブ(例: ガベージコレクション)を定義します。
    • index.yaml: App Engine Datastoreのカスタムインデックスを定義するファイルです。複雑なクエリを実行する際に必要となります。
    • queue.yaml: App Engine Task Queueの設定ファイルで、非同期タスクの処理キューを定義します。
  • Go言語: Googleが開発した静的型付けのコンパイル型言語です。並行処理に強く、シンプルで効率的なコード記述が可能です。このアプリケーションのバックエンドロジックはGoで記述されています。
  • Rietveld: Googleが開発したオープンソースのコードレビューツールです。PerforceやGitなどのバージョン管理システムと連携し、変更の差分表示、コメント、承認などの機能を提供します。このGoコードレビューダッシュボードは、RietveldのAPIを利用してCL情報を取得しています。
  • App Engine Datastore: Google App Engineが提供するNoSQLデータベースサービスです。スキーマレスで、高いスケーラビリティと可用性を提供します。このアプリケーションでは、CL(Change List)の情報を永続化するために使用されています。
  • App Engine Task Queue: App Engineが提供する非同期タスク実行サービスです。時間のかかる処理や、外部サービスへのリクエストなどをバックグラウンドで実行するために使用されます。このアプリケーションでは、CL情報の更新を非同期で行うために利用されています。
  • App Engine Mail API: App Engineアプリケーションがメールを受信するためのAPIです。このアプリケーションでは、コードレビューのメール通知を処理し、関連するCLを更新するために使用されています。
  • App Engine URL Fetch API: App Engineアプリケーションが外部のHTTPリソースにアクセスするためのAPIです。このアプリケーションでは、RietveldのAPIからCL情報を取得するために使用されています。

技術的詳細

このコミットで導入されたGoコードレビューダッシュボードは、Google App Engineの様々なサービスを組み合わせて構築されています。

  1. アプリケーション構造:

    • misc/dashboard/codereview/: アプリケーションのルートディレクトリ。
    • app.yaml: App Engineのメイン設定ファイル。Goランタイムを使用し、メール受信、タスクキュー、管理画面へのアクセス制御、静的ファイルの配信、メインアプリケーションハンドラの設定を行います。
    • cron.yaml: 定期実行タスク(ガベージコレクション)を設定します。
    • queue.yaml: タスクキュー(update-cl)を設定します。
    • index.yaml: Datastoreのクエリに必要なカスタムインデックスを定義します。
    • dashboard/: Go言語のアプリケーションロジックが含まれるパッケージ。
      • cl.go: CL(Change List)エンティティのデータモデル定義と、CLの更新、割り当てに関するハンドラロジック。
      • front.go: フロントページ(ダッシュボード)の表示ロジックと、CLのフィルタリング、表示に関する処理。
      • gc.go: 古いCLをDatastoreから削除するガベージコレクションロジック。
      • mail.go: 受信したメールを解析し、関連するCLの更新をトリガーするロジック。
      • people.go: Goプロジェクトのレビュー担当者(gophers)のリストと、メールアドレスから担当者IDへのマッピングを管理するロジック。
    • static/: 静的ファイル(画像など)を格納。
  2. データモデル (CLエンティティ): dashboard/cl.go で定義される CL 構造体は、コードレビューの変更リストを表します。

    type CL struct {
        Number string // e.g. "5903061"
        Closed bool
        Owner  string // email address
    
        Created, Modified time.Time
    
        Description []byte `datastore:",noindex"`
        FirstLine   string `datastore:",noindex"`
        LGTMs       []string
    
        // These are person IDs (e.g. "rsc"); they may be empty
        Author   string
        Reviewer string
    }
    
    • Number: CLの識別番号。
    • Closed: CLがクローズされているかどうかのフラグ。
    • Owner: CLの作成者のメールアドレス。
    • Created, Modified: 作成日時、最終更新日時。
    • Description, FirstLine: CLの概要。datastore:",noindex" タグにより、Datastoreのインデックス作成対象から除外され、ストレージコストを削減し、書き込みパフォーマンスを向上させます。
    • LGTMs: "Looks Good To Me" (承認) を与えたレビュー担当者のリスト。
    • Author, Reviewer: CLの作成者とレビュー担当者のID(例: "rsc")。
  3. 主要な機能とワークフロー:

    • CL情報の取得と更新:
      • handleUpdateCL 関数(cl.go)が、RietveldのAPI (http://codereview.appspot.com/api/<CL_NUMBER>?messages=true) からCLの詳細情報を取得します。
      • 取得したJSONデータ(説明、作成者、更新日時、クローズ状態、メッセージなど)を解析し、CLエンティティにマッピングします。
      • 特に、メッセージからLGTM(承認)の有無や、レビュー担当者による「Submitted as」メッセージを検出してCLのクローズ状態を更新するロジックが含まれています。
      • 更新されたCLエンティティはDatastoreに保存されます。トランザクション内で既存のレビュー担当者情報を保持しつつ更新を行います。
    • 非同期更新 (UpdateCLLater):
      • UpdateCLLater 関数(cl.go)は、指定されたCLの更新タスクをApp Engine Task Queueに追加します。これにより、CLの更新処理が非同期で行われ、ユーザーリクエストの応答時間を短縮します。
      • メール受信時や、手動での更新トリガー時に利用されます。
    • メール受信処理 (handleMail):
      • mail.gohandleMail 関数は、App Engine Mail APIを通じて受信したメールを処理します。
      • メールの件名からCL番号を正規表現で抽出し、そのCLの更新をUpdateCLLaterを介して非同期でトリガーします。これにより、コードレビューのコメントやステータス変更がメールで通知された際に、ダッシュボードのCL情報が自動的に更新されるようになります。
    • CLの割り当て (handleAssign):
      • cl.gohandleAssign 関数は、フロントエンドからのPOSTリクエストを受け取り、特定のCLにレビュー担当者を割り当てます。
      • Datastoreトランザクション内でCLエンティティを取得し、Reviewerフィールドを更新して保存します。
    • フロントページ表示 (handleFront):
      • front.gohandleFront 関数は、ダッシュボードのメインページをレンダリングします。
      • DatastoreからアクティブなCL(レビュー担当者に割り当てられたCL、自身が作成したCL、その他のアクティブなCL)と、最近クローズされたCLを取得します。
      • 複数のDatastoreクエリを並行して実行するためにsync.WaitGroupを使用し、パフォーマンスを向上させています。
      • 取得したデータはHTMLテンプレートに渡され、動的にページが生成されます。
      • フロントエンドではjQueryを使用して、レビュー担当者の割り当てを非同期で行うJavaScriptロジックが含まれています。
    • ガベージコレクション (handleGC):
      • gc.gohandleGC 関数は、定期的に実行されるCronジョブとして設定されています。
      • Datastoreから、クローズ済みでかつ168時間(7日間)以上更新されていない古いCLを検索し、一括削除します。これにより、Datastoreのデータ量を適切に保ち、コストとパフォーマンスを最適化します。
    • 人物情報管理 (people.go):
      • people.go は、Goプロジェクトの主要なレビュー担当者(gophers)のリストと、彼らのメールアドレス(@golang.org@google.comドメイン)から短いIDへのマッピングを初期化します。これは、CLの所有者やレビュー担当者の表示を簡潔にするために使用されます。

このアプリケーションは、Go言語の並行処理機能(goroutineとchannel)をfront.goでのデータ取得に活用し、App Engineのマネージドサービスを効果的に利用することで、スケーラブルでメンテナンスしやすいコードレビューダッシュボードを実現しています。

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

このコミットでは、以下のファイルが新規追加されています。

  • misc/dashboard/codereview/app.yaml: App Engineアプリケーションのデプロイ設定。
  • misc/dashboard/codereview/cron.yaml: 定期実行ジョブ(ガベージコレクション)の設定。
  • misc/dashboard/codereview/dashboard/cl.go: CLエンティティの定義、CLの更新・割り当てロジック。
  • misc/dashboard/codereview/dashboard/front.go: ダッシュボードのフロントページ表示ロジックとHTMLテンプレート。
  • misc/dashboard/codereview/dashboard/gc.go: 古いCLを削除するガベージコレクションロジック。
  • misc/dashboard/codereview/dashboard/mail.go: 受信メール処理ロジック。
  • misc/dashboard/codereview/dashboard/people.go: レビュー担当者情報の管理ロジック。
  • misc/dashboard/codereview/index.yaml: Datastoreのカスタムインデックス定義。
  • misc/dashboard/codereview/queue.yaml: タスクキューの設定。
  • misc/dashboard/codereview/static/gopherstamp.jpg: 静的画像ファイル。
  • misc/dashboard/codereview/static/icon.png: 静的画像ファイル。

コアとなるコードの解説

misc/dashboard/codereview/dashboard/cl.go

このファイルは、コードレビューの変更リスト(CL)に関する主要なデータモデルと操作を定義しています。

  • CL 構造体: CLの番号、クローズ状態、所有者、作成・更新日時、説明、LGTM(承認)リスト、作成者、レビュー担当者などの情報を保持します。datastore:",noindex" タグは、DescriptionFirstLineフィールドがDatastoreのインデックス作成対象から除外されることを示し、ストレージと書き込みの効率化を図っています。
  • ShortOwner(): CLの所有者のメールアドレスを、people.goで定義された短い担当者IDに変換して表示するためのヘルパーメソッドです。
  • FirstLineHTML() / LGTMHTML(): HTMLテンプレートで表示するために、CLの最初の行やLGTMsリストを適切にエスケープし、整形するためのヘルパーメソッドです。特に、パッケージ名を太字にするなどの表示調整が含まれます。
  • ModifiedAgo(): CLが最後に更新されてからの経過時間を「X分前」のような形式で表示するためのヘルパーメソッドです。
  • handleAssign(w http.ResponseWriter, r *http.Request):
    • /assign エンドポイントのハンドラ。
    • POSTリクエストを受け取り、CL番号と割り当てるレビュー担当者IDをフォーム値から取得します。
    • 現在のユーザーがレビュー担当者リストに含まれているかを確認し、権限がない場合はStatusUnauthorizedを返します。
    • Datastoreトランザクション内で、指定されたCLエンティティを取得し、Reviewerフィールドを更新してDatastoreに保存します。これにより、CLにレビュー担当者を割り当てることができます。
  • UpdateCLLater(c appengine.Context, n string, delay time.Duration):
    • 指定されたCL番号nの更新タスクを、指定されたdelay後にupdate-clという名前のタスクキューに追加します。
    • これは、CL情報の更新を非同期で行うためのメカニズムであり、ユーザーリクエストの応答性を保ちつつ、外部APIからのデータ取得やDatastoreへの書き込みといった時間のかかる処理をバックグラウンドで実行するために使用されます。
  • handleUpdateCL(w http.ResponseWriter, r *http.Request):
    • /update-cl エンドポイントのハンドラ。主にタスクキューから呼び出されます。
    • フォーム値からCL番号を取得し、updateCL関数を呼び出してCL情報を更新します。
    • 更新が成功すれば"OK"を返します。
  • updateCL(c appengine.Context, n string) error:
    • 単一のCLをRietveldのAPIから取得し、Datastoreに保存するコアロジック。
    • codereviewBase + "/api/" + n + "?messages=true" というURLでRietveldのAPIを呼び出し、CLの詳細情報(メッセージを含む)をJSON形式で取得します。
    • 取得したJSONをapiResp構造体にデコードします。
    • apiRespのデータからCLエンティティを構築し、作成日時や更新日時をパースします。
    • CLのDescriptionから最初の行を抽出し、FirstLineとして設定します。
    • CLのメッセージをイテレートし、LGTM(承認)メッセージを検出してLGTMsリストに追加します。
    • レビュー担当者からの「*** Submitted as 」というメッセージを検出した場合、CLがクローズされたと判断し、Closedフラグをtrueに設定します。これは、RietveldのCLがすぐに「クローズ済み」にならない場合のシミュレーションです。
    • Datastoreトランザクション内で、既存のCLエンティティを取得し、レビュー担当者情報(Reviewer)を保持したまま、新しいCL情報で上書き保存します。

misc/dashboard/codereview/dashboard/front.go

このファイルは、ダッシュボードのフロントページ表示に関するロジックを扱います。

  • handleFront(w http.ResponseWriter, r *http.Request):
    • / エンドポイントのハンドラ。
    • frontPageData構造体を初期化し、レビュー担当者リストと現在のユーザーがレビュー担当者であるかどうかのフラグを設定します。
    • Datastoreから以下のCLリストを並行して取得します(sync.WaitGroupを使用):
      • 現在のユーザーに割り当てられたCL(レビュー担当者の場合)
      • 現在のユーザーが作成したCL
      • その他のアクティブなCL(上記2つを除く)
      • 最近クローズされたCL
    • 取得したデータはfrontPageテンプレートに渡され、HTMLが生成されてクライアントに返されます。
  • frontPageData 構造体: フロントページに表示するデータを保持します。clTableの配列と、レビュー担当者リスト、現在のユーザーがレビュー担当者であるかどうかのフラグが含まれます。
  • clTable 構造体: 各CLリスト(テーブル)のタイトル、割り当て可能かどうか、CLのリストを保持します。
  • frontPage テンプレート: HTMLテンプレートを定義します。
    • Go言語のテンプレート構文を使用し、frontPageDataから動的にコンテンツを生成します。
    • CLの所有者、レビュー担当者、タイトル、最終更新日時などを表示します。
    • レビュー担当者の選択ドロップダウン(select要素)が含まれており、JavaScript(jQuery)を使用して、選択が変更された際に/assignエンドポイントに非同期でPOSTリクエストを送信し、CLのレビュー担当者を更新するロジックが埋め込まれています。
    • CLのステータス(保留中、失敗、保存済み)に応じて行の背景色を変更するCSSクラス(pending, failed, saved)が定義されています。

misc/dashboard/codereview/dashboard/gc.go

このファイルは、古いCLのガベージコレクション(GC)ロジックを扱います。

  • handleGC(w http.ResponseWriter, r *http.Request):
    • /gc エンドポイントのハンドラ。cron.yamlで設定されたCronジョブによって定期的に呼び出されます。
    • Datastoreから、クローズ済み(Closed = true)で、かつ168時間(7日間)以上更新されていない(Modified < cutoff)CLを検索します。
    • 最大100件のCLのキーのみを取得し(KeysOnly())、datastore.DeleteMultiを使用してそれらのCLを一括削除します。
    • これにより、Datastoreに不要なデータが蓄積されるのを防ぎ、データベースのパフォーマンスとコストを最適化します。

misc/dashboard/codereview/dashboard/mail.go

このファイルは、App Engine Mail APIを通じて受信したメールを処理します。

  • handleMail(w http.ResponseWriter, r *http.Request):
    • /_ah/mail/ パスへのリクエストを処理するハンドラ。App Engineが受信メールをこのエンドポイントにルーティングします。
    • 受信したメールの本文をmail.ReadMessageでパースします。
    • メールの件名から正規表現.*code review (\\d+):.*を使用してCL番号を抽出します。
    • CL番号が抽出できた場合、UpdateCLLater関数を呼び出し、10秒の遅延を伴ってそのCLの更新タスクをタスクキューに追加します。この遅延は、RietveldがCLの変更を反映するのに十分な時間を与えるためです。
    • これにより、コードレビューのコメントやステータス変更がメールで通知された際に、ダッシュボードのCL情報が自動的に更新されるようになります。

misc/dashboard/codereview/dashboard/people.go

このファイルは、Goプロジェクトのレビュー担当者(gophers)の情報を管理します。

  • emailToPerson マップ: メールアドレス(例: dsymonds@golang.org, rsc@google.com)から、短い担当者ID(例: dsymonds, rsc)へのマッピングを保持します。
  • personList スライス: 短い担当者IDのリストを保持します。
  • init() 関数:
    • パッケージがロードされる際に自動的に実行されます。
    • gophersという配列に、Goプロジェクトの主要なレビュー担当者の短いIDをハードコードしています。
    • これらのIDに対して、@golang.org@google.comの2つのドメインのメールアドレスをemailToPersonマップに登録します。
    • personListをソートし、フロントエンドでの表示順序を決定します。
    • このファイルは、アプリケーション全体で一貫した担当者IDの解決と表示を可能にします。

関連リンク

参考にした情報源リンク