Gmail APIのpush通知が止まったら、watchの期限、Pub/Subへの配送、通知受信後の差分処理を別々に確認します。watchを更新できたことは、止まっていた間の変更を取り込めた証拠にはなりません。最後に処理済みと確定したhistoryIdから変更を読み、期限外なら必要範囲を再同期します。
この記事は、既存のGmail API同期アプリの診断手順です。メール本文の取得、watch登録・更新、Pub/Sub設定変更、メール送信は実施していません。以下の時刻や状態は説明用の想定で、実環境の復旧成功を報告するものではありません。
目次
最後の通知と最後の同期完了を並べる
対象のGmail user、OAuth client、Cloud project、topic、subscription、受信endpointを確認します。開発と本番、別userのwatchを混ぜると、通知が届いているように見えても同期先が違います。tokenやメール本文ではなく、識別に必要な環境名と処理時刻を記録します。
表は横にスクロールできます
| 確認する記録 | 分かる範囲 | それだけでは分からないこと |
|---|---|---|
| watchのexpiration | 通知契約の期限 | 履歴が保持される期限 |
| Pub/Sub受信時刻 | 配送経路が動いたか | メール変更の反映完了 |
| handlerのack | 配送メッセージの受付 | 業務処理全体の成功 |
| 最後の処理済historyId | 同期を進めた位置 | watch更新時点までの同期 |
| 差分処理の最終成功 | 保存処理が完了した時点 | 以後の通知に欠落がないこと |
通知がない原因を、すぐOAuthの失効と決めません。API自体が認証エラーになるのか、通知だけ来ないのかを分けます。
watch期限と対象条件を確認する
公式pushガイド(新しいタブで開く)は、少なくとも7日ごとのwatch更新が必要で、毎日の更新を推奨し、応答のexpirationで期限を確認するよう案内しています。固定の日数だけを頼りにせず、実際に更新が成功したかと次の期限を保存します。
更新jobが実行されたlogがあっても、watch要求が失敗していれば期限は延びません。topic、label条件、対象userが元と一致するかを照合します。通知対象に合う変更が起きていないだけなら、通知の少なさを故障と判定しないようにします。
watch成功直後の通知も、欠落期間の業務処理が完了した合図ではありません。既存の同期位置を、新しいwatch応答のhistoryIdへ無条件に上書きしないことが重要です。
配送と受信後の保存を分けて調べる
Pub/Subのtopicとsubscription、pushかpullか、受信側の応答、再配送の記録を確認します。watch要求のエラーがtopicの設定を示すなら、その原因を担当者と確認します。権限や公開範囲を広げる前に、失敗している経路を限定します。
Gmailの通知dataをdecodeすると、emailAddressとhistoryIdが得られます。Pub/SubのmessageIdはGmailのメールIDではありません。通知を一通のメール本文と解釈せず、同期を起動する手掛かりとして扱います。
ackを返す前に処理をすべて終える設計か、耐久queueへ保存してからackする設計かも確認します。後者なら、queue保存成功と差分処理成功を別に監視します。ack後にメモリ上の処理が失われると、配送は成功でも同期は抜けるためです。これはアプリ設計の確認点で、Gmailが自動で業務処理を保証するという意味ではありません。
処理済historyIdから履歴を全ページ読む
users.history.list(新しいタブで開く)へ、最後に処理済みと確定した値をstartHistoryIdとして渡します。履歴IDは連番ではないため、数値に1を足して次の位置を作ったり、ID差から未処理件数を算出したりしません。大きな数値を精度の落ちる型へ変換せず、APIの文字列として保持します。
nextPageTokenがある間は続きを取得し、追加・削除・label変更など必要な変更をローカル状態へ反映します。途中で失敗したら、終端のhistoryIdを処理済みとして確定しません。最終ページまでの反映と、同期位置の更新が食い違わない保存方式を選びます。
表は横にスクロールできます
| 想定される中断 | 避けたい結果 | 設計で確認すること |
|---|---|---|
| 2ページ目で失敗 | 未取得分を飛ばす | checkpointを早めない |
| 保存後・位置更新前に停止 | 同じ変更を二重適用 | IDベースの更新と再処理 |
| 同時workerが同じuserを処理 | 古い位置で上書き | user単位の排他・順序 |
| 同じ通知が再配送 | 業務操作を再実行 | 同期と送信等の副作用を分離 |
履歴の一般messages欄と変更種別の欄に同じmessageが現れる場合があります。各項目を別メールと数えず、必要な変更種別とmessage IDを基準に処理します。
履歴期限外の404なら再同期へ切り替える
公式同期ガイド(新しいタブで開く)は、startHistoryIdが取得可能な履歴の範囲外なら404となり、full syncが必要と説明しています。watchの期限と履歴の保持は別で、watchを更新しても古い履歴を復活させる操作にはなりません。すべてのGmail APIの404を同じ理由にするのではなく、失敗したhistory.listと指定値を確認します。
再同期は、アプリの目的に必要なmessageやthreadの範囲を取り直す作業です。Gmail上のメールを削除する処理ではありません。元のローカル複製、独自メモ、未送信の業務queueを分け、必要なデータを消さずに作り直せる方式を選びます。
再構築中の空配列を「受信メールなし」と利用側へ公開しないよう、同期中・失敗・完了を区別します。取得範囲とページングを完了し、採用する新しい履歴位置を確定した後、通常の差分同期へ戻します。
通知が欠けても同期できるか確認する
公式pushガイドは、通知が遅延・欠落する場合があるため、無通知期間後にhistory.listを呼ぶなどの補完を案内しています。補完頻度は必要な鮮度、API利用量、処理時間に合わせます。監視を増やすだけで同時処理を増幅しないよう、通常通知と同じ同期queueを使う設計を検討します。
許可された検証では、watch更新後の通知、既知の変更の取得、複数ページ、再配送、途中停止後の再開、履歴期限外の分岐を別に確認します。メールを送ったり大量の変更を起こしたりして本番で障害を再現する必要はありません。mockと実際の許可された読取で何を確認したかを区別します。
復旧完了の記録には、watchの次回期限、配送経路、同期位置、取り戻した対象範囲、未確認の期間を残します。「通知が1件来た」から「停止期間も同期できた」へ飛躍しないことが、再発時の診断にも役立ちます。
