[インデックス 12996] ファイルの概要
このコミットは、Goプロジェクトのコードレビューダッシュボードシステムにおいて、コードレビューのスレッドメールのMessage-ID
を記録するように変更を加えるものです。これにより、将来的に「R=...」形式の返信メールを適切にスレッド化(関連付ける)できるようになるための基盤を構築します。具体的には、CL
構造体にLastMessageID
フィールドを追加し、受信したメールのMessage-ID
をデータストアに保存するロジックが導入されています。
コミット
commit 1bdb788b2ea5147ff7847f7a401a9da994a5e360
Author: David Symonds <dsymonds@golang.org>
Date: Mon Apr 30 22:47:51 2012 +1000
misc/dashboard/codereview: record Message-ID of code review thread mails.
This will allow us to properly thread "R=..." mails at a later time.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/6135053
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/1bdb788b2ea5147ff7847f7a401a9da994a5e360
元コミット内容
misc/dashboard/codereview: record Message-ID of code review thread mails.
This will allow us to properly thread "R=..." mails at a later time.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/6135053
変更の背景
この変更の背景には、コードレビュープロセスにおけるメール通知の管理と、それらのメールを論理的な会話スレッドとして適切に扱う必要性があります。Goプロジェクトでは、Rietveld(Googleが開発したコードレビューツール)をベースとしたシステムが使用されており、コードレビューのコメントやステータス変更はメールで通知されます。
従来のシステムでは、これらのメールが必ずしも適切にスレッド化されていなかった可能性があります。特に、レビュー担当者からの「R=...」(Reviewed=...)といった形式の返信メールは、元のレビューコメントや変更リスト(CL: Change List)と関連付けられず、独立したメールとして扱われてしまうことが問題でした。
メールのスレッド化は、関連するメールをグループ化して表示することで、ユーザーが特定の話題やコードレビューの進捗を追いやすくするために非常に重要です。これを実現するためには、メールヘッダのMessage-ID
とIn-Reply-To
フィールドが鍵となります。Message-ID
は各メールに一意に割り当てられる識別子であり、In-Reply-To
は返信元のメールのMessage-ID
を指します。これにより、メールクライアントは関連するメールをスレッドとして表示できます。
このコミットは、将来的にIn-Reply-To
ヘッダを適切に設定できるようにするための第一歩として、まずコードレビュー関連のメールのMessage-ID
をシステム側で記録・保存することを目的としています。これにより、後続の変更でappengine/mail
パッケージがIn-Reply-To
ヘッダの設定をサポートするようになった際に、スムーズにスレッド化機能を実現できるようになります。
前提知識の解説
1. Rietveld (コードレビューシステム)
Rietveldは、Googleが開発したWebベースのコードレビューシステムです。PerforceやGitなどのバージョン管理システムと連携し、変更差分(diff)の表示、コメントの追加、レビューの承認(LGTM: Looks Good To Me)などの機能を提供します。Goプロジェクトのコードレビューシステムは、このRietveldをベースに構築されています。レビューの通知は通常、メールで行われます。
2. Google App Engine (GAE)
Google App Engineは、Googleが提供するPaaS(Platform as a Service)です。開発者はインフラの管理を気にすることなく、アプリケーションをデプロイ・実行できます。Goプロジェクトのコードレビューダッシュボードは、Google App Engine上で動作していると考えられます。GAEは、データストア(NoSQLデータベース)やメール送信サービスなど、様々なAPIを提供しています。
3. appengine/datastore
Google App Engineのデータストアは、スケーラブルなNoSQLデータベースサービスです。Go言語からはappengine/datastore
パッケージを通じてアクセスできます。データは「エンティティ」として保存され、各エンティティは「種類(Kind)」と一意の「キー」を持ちます。このコミットでは、CL
(Change List)エンティティに新しいフィールドを追加し、その値を保存するためにデータストアが利用されています。
4. Message-ID
と In-Reply-To
(メールヘッダ)
Message-ID
: 各メールに一意に割り当てられる識別子です。通常、<一意な文字列@ドメイン>
のような形式をしています。メールクライアントやMTA(Mail Transfer Agent)によって自動的に生成されます。In-Reply-To
: このメールがどのメールへの返信であるかを示すヘッダです。返信元のメールのMessage-ID
がここに記述されます。References
:In-Reply-To
と同様にスレッド化に利用されますが、こちらはスレッド内の過去のすべてのMessage-ID
をリストします。
これらのヘッダを適切に利用することで、メールクライアントは関連するメールをグループ化し、会話の流れを追跡しやすくします。
5. 「R=...」メール
Goプロジェクトのコードレビューでは、レビュー担当者が変更を承認した際に「R=...」という形式のコメントをメールで送ることがあります。これは「Reviewed by ...」を意味し、レビューが完了したことを示します。このコミットの目的は、これらのメールが元のコードレビューのスレッドに正しく関連付けられるようにすることです。
技術的詳細
このコミットは、Goプロジェクトのコードレビューダッシュボードのバックエンドにおけるデータモデルとメール処理ロジックに焦点を当てています。
1. CL
構造体へのLastMessageID
の追加
misc/dashboard/codereview/dashboard/cl.go
ファイルにおいて、CL
構造体(Change List、つまりコードレビュー対象の変更セットを表すデータモデル)にLastMessageID
という新しいフィールドが追加されました。
type CL struct {
// ... 既存のフィールド ...
// Mail information.
Subject string `datastore:",noindex"`
Recipients []string `datastore:",noindex"`
LastMessageID string `datastore:",noindex"` // <-- 追加
// ... 既存のフィールド ...
}
このフィールドは、そのCL
に関連する最新のメールのMessage-ID
を保存するために使用されます。datastore:",noindex"
タグは、このフィールドがデータストアのインデックス作成の対象外であることを示しており、クエリのパフォーマンスには影響せず、単に値を保存する目的であることを意味します。
2. LastMessageID
の保存ロジック
misc/dashboard/codereview/dashboard/mail.go
ファイルにおいて、受信したメールを処理するhandleMail
関数内で、メールのMessage-ID
を対応するCL
エンティティのLastMessageID
フィールドに保存するロジックが追加されました。
func handleMail(w http.ResponseWriter, r *http.Request) {
// ... 既存の処理 ...
// Track the MessageID.
key := datastore.NewKey(c, "CL", m[1], 0, nil)
err = datastore.RunInTransaction(c, func(c appengine.Context) error {
cl := new(CL)
err := datastore.Get(c, key, cl)
if err != nil && err != datastore.ErrNoSuchEntity {
return err
}
cl.LastMessageID = msg.Header.Get("Message-ID") // <-- ここでMessage-IDを取得し設定
_, err = datastore.Put(c, key, cl)
return err
}, nil)
if err != nil {
c.Errorf("datastore transaction failed: %v", err)
}
// ... 既存の処理 ...
}
この処理はトランザクション内で行われます。これは、データストアへの書き込みがアトミックに行われることを保証するためです。つまり、CL
エンティティの取得、LastMessageID
の更新、そしてエンティティの保存が、すべて成功するか、すべて失敗するかのいずれかになります。これにより、データの整合性が保たれます。
msg.Header.Get("Message-ID")
は、受信したメールのヘッダからMessage-ID
フィールドの値を取得しています。
3. LastMessageID
の永続化
misc/dashboard/codereview/dashboard/cl.go
のupdateCL
関数では、CL
エンティティを更新する際に、既存のLastMessageID
が失われないようにする変更が加えられました。
func updateCL(c appengine.Context, n string) error {
// ... 既存の処理 ...
} else if err == nil {
// LastMessageID and Reviewer need preserving.
cl.LastMessageID = ocl.LastMessageID // <-- 既存のLastMessageIDを保持
cl.Reviewer = ocl.Reviewer
}
// ... 既存の処理 ...
}
これは、CL
エンティティがデータストアから読み込まれ、何らかの更新が行われる際に、LastMessageID
フィールドが誤って上書きされたり、空になったりするのを防ぐためのものです。これにより、一度保存されたLastMessageID
が、その後のCL
の更新プロセスで永続的に保持されることが保証されます。
4. 将来の機能拡張への言及
misc/dashboard/codereview/dashboard/cl.go
のhandleAssign
関数には、将来的な機能拡張に関するコメントが追加されています。
// TODO(dsymonds): Use cl.LastMessageID as the In-Reply-To header
// when the appengine/mail package supports that.
このコメントは、このコミットがMessage-ID
を記録する基盤を構築するものであり、実際にメールのスレッド化を実現するためには、Google App Engineのメール送信API(appengine/mail
)がIn-Reply-To
ヘッダの設定をサポートする必要があることを示唆しています。このコミット時点ではその機能が利用できなかったため、将来の課題として残されています。
コアとなるコードの変更箇所
misc/dashboard/codereview/dashboard/cl.go
--- a/misc/dashboard/codereview/dashboard/cl.go
+++ b/misc/dashboard/codereview/dashboard/cl.go
@@ -45,8 +45,9 @@ type CL struct {
LGTMs []string
// Mail information.
- Subject string `datastore:",noindex"`
- Recipients []string `datastore:",noindex"`
+ Subject string `datastore:",noindex"`
+ Recipients []string `datastore:",noindex"`
+ LastMessageID string `datastore:",noindex"`
// These are person IDs (e.g. "rsc"); they may be empty
Author string
@@ -193,6 +194,8 @@ func handleAssign(w http.ResponseWriter, r *http.Request) {
Subject: cl.Subject + " (issue " + n + ")",
Body: "R=" + rev + "\n\n(sent by gocodereview)",
}
+ // TODO(dsymonds): Use cl.LastMessageID as the In-Reply-To header
+ // when the appengine/mail package supports that.
sendMailLater.Call(c, msg)
}
}
@@ -339,7 +342,8 @@ func updateCL(c appengine.Context, n string) error {\
if err != nil && err != datastore.ErrNoSuchEntity {
return err
} else if err == nil {
- // Reviewer is the only field that needs preserving.
+ // LastMessageID and Reviewer need preserving.
+ cl.LastMessageID = ocl.LastMessageID
cl.Reviewer = ocl.Reviewer
}
_, err = datastore.Put(c, key, cl)
misc/dashboard/codereview/dashboard/mail.go
--- a/misc/dashboard/codereview/dashboard/mail.go
+++ b/misc/dashboard/codereview/dashboard/mail.go
@@ -9,6 +9,7 @@ import (
"time"
"appengine"
+ "appengine/datastore"
)
func init() {
@@ -35,6 +36,23 @@ func handleMail(w http.ResponseWriter, r *http.Request) {
}
c.Infof("Found issue %q", m[1])
+
+ // Track the MessageID.
+ key := datastore.NewKey(c, "CL", m[1], 0, nil)
+ err = datastore.RunInTransaction(c, func(c appengine.Context) error {
+ cl := new(CL)
+ err := datastore.Get(c, key, cl)
+ if err != nil && err != datastore.ErrNoSuchEntity {
+ return err
+ }
+ cl.LastMessageID = msg.Header.Get("Message-ID")
+ _, err = datastore.Put(c, key, cl)
+ return err
+ }, nil)
+ if err != nil {
+ c.Errorf("datastore transaction failed: %v", err)
+ }
+
// Update the CL after a delay to give Rietveld a chance to catch up.
UpdateCLLater(c, m[1], 10*time.Second)
}
コアとなるコードの解説
misc/dashboard/codereview/dashboard/cl.go
-
CL
構造体へのLastMessageID
フィールドの追加:LastMessageID string
datastore:",noindex":
CL構造体に新しいフィールドが追加されました。このフィールドは、そのコードレビュー(CL)に関連する最新のメールの
Message-IDを文字列として保存します。
datastore:",noindex"`タグは、このフィールドがデータストアのインデックス作成の対象外であることを示し、単に値を保存する目的であることを明確にしています。
-
handleAssign
関数内のTODOコメント:// TODO(dsymonds): Use cl.LastMessageID as the In-Reply-To header // when the appengine/mail package supports that.
- このコメントは、将来的に
appengine/mail
パッケージがメールのIn-Reply-To
ヘッダを設定する機能をサポートするようになった際に、保存されたLastMessageID
を使用してメールをスレッド化する計画があることを示しています。このコミット時点では、その機能が利用できないため、基盤の準備に留まっています。
-
updateCL
関数でのLastMessageID
の保持:cl.LastMessageID = ocl.LastMessageID
:CL
エンティティをデータストアに保存する際に、既存のLastMessageID
が誤って上書きされないように、古いCL
オブジェクト(ocl
)からLastMessageID
をコピーして保持しています。これにより、一度記録されたMessage-ID
が、その後のCL
の更新プロセスで失われることなく永続的に保持されることが保証されます。
misc/dashboard/codereview/dashboard/mail.go
-
appengine/datastore
パッケージのインポート:"appengine/datastore"
: データストア操作を行うために必要なパッケージがインポートされました。
-
handleMail
関数でのMessage-ID
の保存ロジック:key := datastore.NewKey(c, "CL", m[1], 0, nil)
: 受信したメールがどのCLに関連するものかを特定するために、CLのキーを生成しています。m[1]
はメールの件名などから抽出されたCLの識別子(issue番号など)と考えられます。err = datastore.RunInTransaction(c, func(c appengine.Context) error { ... }, nil)
: データストアへの書き込み操作をトランザクション内で実行しています。これにより、複数の操作がアトミックに(すべて成功するか、すべて失敗するかのいずれかで)実行され、データの整合性が保たれます。cl := new(CL)
: 新しいCL
構造体のインスタンスを作成します。err := datastore.Get(c, key, cl)
: 指定されたキーに対応するCL
エンティティをデータストアから取得します。もしエンティティが存在しない場合(ErrNoSuchEntity
)、それは新しいCLであると見なされ、エラーとはなりません。cl.LastMessageID = msg.Header.Get("Message-ID")
: 受信したメール(msg
)のヘッダからMessage-ID
を取得し、それをCL
オブジェクトのLastMessageID
フィールドに設定します。_, err = datastore.Put(c, key, cl)
: 更新されたCL
オブジェクトをデータストアに保存します。
この一連の変更により、コードレビューダッシュボードは、各コードレビューに関連する最新のメールのMessage-ID
を追跡し、保存できるようになりました。これは、将来的にメールのスレッド化機能を実装するための重要なステップとなります。
関連リンク
- Rietveld: https://code.google.com/p/rietveld/ (現在はアーカイブされていますが、情報源として)
- Google App Engine: https://cloud.google.com/appengine
- Go App Engine Datastore: https://cloud.google.com/appengine/docs/standard/go/datastore
- RFC 5322 (Internet Message Format) - Message-ID and In-Reply-To: https://datatracker.ietf.org/doc/html/rfc5322#section-3.6.4
参考にした情報源リンク
- https://github.com/golang/go/commit/1bdb788b2ea5147ff7847f7a401a9da994a5e360
- Google検索: "Rietveld code review system"
- Google検索: "Google App Engine mail API In-Reply-To"
- Google検索: "email threading Message-ID In-Reply-To"
- Go言語の
appengine/datastore
に関する一般的な知識 - メールヘッダ(
Message-ID
,In-Reply-To
)に関する一般的な知識 - トランザクション処理に関する一般的な知識# [インデックス 12996] ファイルの概要
このコミットは、Goプロジェクトのコードレビューダッシュボードシステムにおいて、コードレビューのスレッドメールのMessage-ID
を記録するように変更を加えるものです。これにより、将来的に「R=...」形式の返信メールを適切にスレッド化(関連付ける)できるようになるための基盤を構築します。具体的には、CL
構造体にLastMessageID
フィールドを追加し、受信したメールのMessage-ID
をデータストアに保存するロジックが導入されています。
コミット
commit 1bdb788b2ea5147ff7847f7a401a9da994a5e360
Author: David Symonds <dsymonds@golang.org>
Date: Mon Apr 30 22:47:51 2012 +1000
misc/dashboard/codereview: record Message-ID of code review thread mails.
This will allow us to properly thread "R=..." mails at a later time.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/6135053
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/1bdb788b2ea5147ff7847f7a401a9da994a5e360
元コミット内容
misc/dashboard/codereview: record Message-ID of code review thread mails.
This will allow us to properly thread "R=..." mails at a later time.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/6135053
変更の背景
この変更の背景には、コードレビュープロセスにおけるメール通知の管理と、それらのメールを論理的な会話スレッドとして適切に扱う必要性があります。Goプロジェクトでは、Rietveld(Googleが開発したコードレビューツール)をベースとしたシステムが使用されており、コードレビューのコメントやステータス変更はメールで通知されます。
従来のシステムでは、これらのメールが必ずしも適切にスレッド化されていなかった可能性があります。特に、レビュー担当者からの「R=...」(Reviewed=...)といった形式の返信メールは、元のレビューコメントや変更リスト(CL: Change List)と関連付けられず、独立したメールとして扱われてしまうことが問題でした。
メールのスレッド化は、関連するメールをグループ化して表示することで、ユーザーが特定の話題やコードレビューの進捗を追いやすくするために非常に重要です。これを実現するためには、メールヘッダのMessage-ID
とIn-Reply-To
フィールドが鍵となります。Message-ID
は各メールに一意に割り当てられる識別子であり、In-Reply-To
は返信元のメールのMessage-ID
を指します。これにより、メールクライアントは関連するメールをスレッドとして表示できます。
このコミットは、将来的にIn-Reply-To
ヘッダを適切に設定できるようにするための第一歩として、まずコードレビュー関連のメールのMessage-ID
をシステム側で記録・保存することを目的としています。これにより、後続の変更でappengine/mail
パッケージがIn-Reply-To
ヘッダの設定をサポートするようになった際に、スムーズにスレッド化機能を実現できるようになります。
前提知識の解説
1. Rietveld (コードレビューシステム)
Rietveldは、Googleが開発したWebベースのコードレビューシステムです。PerforceやGitなどのバージョン管理システムと連携し、変更差分(diff)の表示、コメントの追加、レビューの承認(LGTM: Looks Good To Me)などの機能を提供します。Goプロジェクトのコードレビューシステムは、このRietveldをベースに構築されています。レビューの通知は通常、メールで行われます。
2. Google App Engine (GAE)
Google App Engineは、Googleが提供するPaaS(Platform as a Service)です。開発者はインフラの管理を気にすることなく、アプリケーションをデプロイ・実行できます。Goプロジェクトのコードレビューダッシュボードは、Google App Engine上で動作していると考えられます。GAEは、データストア(NoSQLデータベース)やメール送信サービスなど、様々なAPIを提供しています。
3. appengine/datastore
Google App Engineのデータストアは、スケーラブルなNoSQLデータベースサービスです。Go言語からはappengine/datastore
パッケージを通じてアクセスできます。データは「エンティティ」として保存され、各エンティティは「種類(Kind)」と一意の「キー」を持ちます。このコミットでは、CL
(Change List)エンティティに新しいフィールドを追加し、その値を保存するためにデータストアが利用されています。
4. Message-ID
と In-Reply-To
(メールヘッダ)
Message-ID
: 各メールに一意に割り当てられる識別子です。通常、<一意な文字列@ドメイン>
のような形式をしています。メールクライアントやMTA(Mail Transfer Agent)によって自動的に生成されます。In-Reply-To
: このメールがどのメールへの返信であるかを示すヘッダです。返信元のメールのMessage-ID
がここに記述されます。References
:In-Reply-To
と同様にスレッド化に利用されますが、こちらはスレッド内の過去のすべてのMessage-ID
をリストします。
これらのヘッダを適切に利用することで、メールクライアントは関連するメールをグループ化し、会話の流れを追跡しやすくします。
5. 「R=...」メール
Goプロジェクトのコードレビューでは、レビュー担当者が変更を承認した際に「R=...」という形式のコメントをメールで送ることがあります。これは「Reviewed by ...」を意味し、レビューが完了したことを示します。このコミットの目的は、これらのメールが元のコードレビューのスレッドに正しく関連付けられるようにすることです。
技術的詳細
このコミットは、Goプロジェクトのコードレビューダッシュボードのバックエンドにおけるデータモデルとメール処理ロジックに焦点を当てています。
1. CL
構造体へのLastMessageID
の追加
misc/dashboard/codereview/dashboard/cl.go
ファイルにおいて、CL
構造体(Change List、つまりコードレビュー対象の変更セットを表すデータモデル)にLastMessageID
という新しいフィールドが追加されました。
type CL struct {
// ... 既存のフィールド ...
// Mail information.
Subject string `datastore:",noindex"`
Recipients []string `datastore:",noindex"`
LastMessageID string `datastore:",noindex"` // <-- 追加
// ... 既存のフィールド ...
}
このフィールドは、そのCL
に関連する最新のメールのMessage-ID
を保存するために使用されます。datastore:",noindex"
タグは、このフィールドがデータストアのインデックス作成の対象外であることを示しており、クエリのパフォーマンスには影響せず、単に値を保存する目的であることを意味します。
2. LastMessageID
の保存ロジック
misc/dashboard/codereview/dashboard/mail.go
ファイルにおいて、受信したメールを処理するhandleMail
関数内で、メールのMessage-ID
を対応するCL
エンティティのLastMessageID
フィールドに保存するロジックが追加されました。
func handleMail(w http.ResponseWriter, r *http.Request) {
// ... 既存の処理 ...
// Track the MessageID.
key := datastore.NewKey(c, "CL", m[1], 0, nil)
err = datastore.RunInTransaction(c, func(c appengine.Context) error {
cl := new(CL)
err := datastore.Get(c, key, cl)
if err != nil && err != datastore.ErrNoSuchEntity {
return err
}
cl.LastMessageID = msg.Header.Get("Message-ID") // <-- ここでMessage-IDを取得し設定
_, err = datastore.Put(c, key, cl)
return err
}, nil)
if err != nil {
c.Errorf("datastore transaction failed: %v", err)
}
// ... 既存の処理 ...
}
この処理はトランザクション内で行われます。これは、データストアへの書き込みがアトミックに行われることを保証するためです。つまり、CL
エンティティの取得、LastMessageID
の更新、そしてエンティティの保存が、すべて成功するか、すべて失敗するかのいずれかになります。これにより、データの整合性が保たれます。
msg.Header.Get("Message-ID")
は、受信したメールのヘッダからMessage-ID
フィールドの値を取得しています。
3. LastMessageID
の永続化
misc/dashboard/codereview/dashboard/cl.go
のupdateCL
関数では、CL
エンティティを更新する際に、既存のLastMessageID
が失われないようにする変更が加えられました。
func updateCL(c appengine.Context, n string) error {
// ... 既存の処理 ...
} else if err == nil {
// LastMessageID and Reviewer need preserving.
cl.LastMessageID = ocl.LastMessageID // <-- 既存のLastMessageIDを保持
cl.Reviewer = ocl.Reviewer
}
// ... 既存の処理 ...
}
これは、CL
エンティティがデータストアから読み込まれ、何らかの更新が行われる際に、LastMessageID
フィールドが誤って上書きされたり、空になったりするのを防ぐためのものです。これにより、一度保存されたLastMessageID
が、その後のCL
の更新プロセスで永続的に保持されることが保証されます。
4. 将来の機能拡張への言及
misc/dashboard/codereview/dashboard/cl.go
のhandleAssign
関数には、将来的な機能拡張に関するコメントが追加されています。
// TODO(dsymonds): Use cl.LastMessageID as the In-Reply-To header
// when the appengine/mail package supports that.
このコメントは、このコミットがMessage-ID
を記録する基盤を構築するものであり、実際にメールのスレッド化を実現するためには、Google App Engineのメール送信API(appengine/mail
)がIn-Reply-To
ヘッダの設定をサポートする必要があることを示唆しています。このコミット時点ではその機能が利用できなかったため、将来の課題として残されています。
コアとなるコードの変更箇所
misc/dashboard/codereview/dashboard/cl.go
--- a/misc/dashboard/codereview/dashboard/cl.go
+++ b/misc/dashboard/codereview/dashboard/cl.go
@@ -45,8 +45,9 @@ type CL struct {
LGTMs []string
// Mail information.
- Subject string `datastore:",noindex"`
- Recipients []string `datastore:",noindex"`
+ Subject string `datastore:",noindex"`
+ Recipients []string `datastore:",noindex"`
+ LastMessageID string `datastore:",noindex"`
// These are person IDs (e.g. "rsc"); they may be empty
Author string
@@ -193,6 +194,8 @@ func handleAssign(w http.ResponseWriter, r *http.Request) {
Subject: cl.Subject + " (issue " + n + ")",
Body: "R=" + rev + "\n\n(sent by gocodereview)",
}
+ // TODO(dsymonds): Use cl.LastMessageID as the In-Reply-To header
+ // when the appengine/mail package supports that.
sendMailLater.Call(c, msg)
}
}
@@ -339,7 +342,8 @@ func updateCL(c appengine.Context, n string) error {\
if err != nil && err != datastore.ErrNoSuchEntity {
return err
} else if err == nil {
- // Reviewer is the only field that needs preserving.
+ // LastMessageID and Reviewer need preserving.
+ cl.LastMessageID = ocl.LastMessageID
cl.Reviewer = ocl.Reviewer
}
_, err = datastore.Put(c, key, cl)
misc/dashboard/codereview/dashboard/mail.go
--- a/misc/dashboard/codereview/dashboard/mail.go
+++ b/misc/dashboard/codereview/dashboard/mail.go
@@ -9,6 +9,7 @@ import (
"time"
"appengine"
+ "appengine/datastore"
)
func init() {
@@ -35,6 +36,23 @@ func handleMail(w http.ResponseWriter, r *http.Request) {
}
c.Infof("Found issue %q", m[1])
+
+ // Track the MessageID.
+ key := datastore.NewKey(c, "CL", m[1], 0, nil)
+ err = datastore.RunInTransaction(c, func(c appengine.Context) error {
+ cl := new(CL)
+ err := datastore.Get(c, key, cl)
+ if err != nil && err != datastore.ErrNoSuchEntity {
+ return err
+ }
+ cl.LastMessageID = msg.Header.Get("Message-ID")
+ _, err = datastore.Put(c, key, cl)
+ return err
+ }, nil)
+ if err != nil {
+ c.Errorf("datastore transaction failed: %v", err)
+ }
+
// Update the CL after a delay to give Rietveld a chance to catch up.
UpdateCLLater(c, m[1], 10*time.Second)
}
コアとなるコードの解説
misc/dashboard/codereview/dashboard/cl.go
-
CL
構造体へのLastMessageID
フィールドの追加:LastMessageID string
datastore:",noindex":
CL構造体に新しいフィールドが追加されました。このフィールドは、そのコードレビュー(CL)に関連する最新のメールの
Message-IDを文字列として保存します。
datastore:",noindex"`タグは、このフィールドがデータストアのインデックス作成の対象外であることを示し、単に値を保存する目的であることを明確にしています。
-
handleAssign
関数内のTODOコメント:// TODO(dsymonds): Use cl.LastMessageID as the In-Reply-To header // when the appengine/mail package supports that.
- このコメントは、将来的に
appengine/mail
パッケージがメールのIn-Reply-To
ヘッダを設定する機能をサポートするようになった際に、保存されたLastMessageID
を使用してメールをスレッド化する計画があることを示しています。このコミット時点では、その機能が利用できないため、基盤の準備に留まっています。
-
updateCL
関数でのLastMessageID
の保持:cl.LastMessageID = ocl.LastMessageID
:CL
エンティティをデータストアに保存する際に、既存のLastMessageID
が誤って上書きされないように、古いCL
オブジェクト(ocl
)からLastMessageID
をコピーして保持しています。これにより、一度記録されたMessage-ID
が、その後のCL
の更新プロセスで失われることなく永続的に保持されることが保証されます。
misc/dashboard/codereview/dashboard/mail.go
-
appengine/datastore
パッケージのインポート:"appengine/datastore"
: データストア操作を行うために必要なパッケージがインポートされました。
-
handleMail
関数でのMessage-ID
の保存ロジック:key := datastore.NewKey(c, "CL", m[1], 0, nil)
: 受信したメールがどのCLに関連するものかを特定するために、CLのキーを生成しています。m[1]
はメールの件名などから抽出されたCLの識別子(issue番号など)と考えられます。err = datastore.RunInTransaction(c, func(c appengine.Context) error { ... }, nil)
: データストアへの書き込み操作をトランザクション内で実行しています。これにより、複数の操作がアトミックに(すべて成功するか、すべて失敗するかのいずれかで)実行され、データの整合性が保たれます。cl := new(CL)
: 新しいCL
構造体のインスタンスを作成します。err := datastore.Get(c, key, cl)
: 指定されたキーに対応するCL
エンティティをデータストアから取得します。もしエンティティが存在しない場合(ErrNoSuchEntity
)、それは新しいCLであると見なされ、エラーとはなりません。cl.LastMessageID = msg.Header.Get("Message-ID")
: 受信したメール(msg
)のヘッダからMessage-ID
を取得し、それをCL
オブジェクトのLastMessageID
フィールドに設定します。_, err = datastore.Put(c, key, cl)
: 更新されたCL
オブジェクトをデータストアに保存します。
この一連の変更により、コードレビューダッシュボードは、各コードレビューに関連する最新のメールのMessage-ID
を追跡し、保存できるようになりました。これは、将来的にメールのスレッド化機能を実装するための重要なステップとなります。
関連リンク
- Rietveld: https://code.google.com/p/rietveld/ (現在はアーカイブされていますが、情報源として)
- Google App Engine: https://cloud.google.com/appengine
- Go App Engine Datastore: https://cloud.google.com/appengine/docs/standard/go/datastore
- RFC 5322 (Internet Message Format) - Message-ID and In-Reply-To: https://datatracker.ietf.org/doc/html/rfc5322#section-3.6.4
参考にした情報源リンク
- https://github.com/golang/go/commit/1bdb788b2ea5147ff7847f7a401a9da994a5e360
- Google検索: "Rietveld code review system"
- Google検索: "Google App Engine mail API In-Reply-To"
- Google検索: "email threading Message-ID In-Reply-To"
- Go言語の
appengine/datastore
に関する一般的な知識 - メールヘッダ(
Message-ID
,In-Reply-To
)に関する一般的な知識 - トランザクション処理に関する一般的な知識