Google Drive APIの変更通知を取りこぼさない|Watch期限とchanges.list

Google Drive APIのpush通知は、変更内容そのものを届ける仕組みではなく、「変更ログを確認して」という合図です。

AIものさし編集部

Google Drive APIのpush通知は、変更内容そのものを届ける仕組みではなく、「変更ログを確認して」という合図です。通知だけを処理済みにして変更データを取得しなかったり、期限切れ後の再watchを忘れたりすると、同期漏れが起きます。安全に使うには、changes APIのpage tokenを正本のカーソルとして保存し、通知のたびにchanges.listを最後まで取り込み、全ページの処理が確定してから新しいstart tokenへ進めます。

まず区別する:通知・変更ログ・カーソル

Drive APIにはfiles.watchとchanges.watchがあり、監視対象と受け取る通知の単位が異なります。ファイルごとの状態を監視したいのか、ユーザーの変更ログを同期したいのかを先に決めます。複数ファイルを継続同期する設計では、個々の通知を変更一覧の代わりに扱わず、changes.watchとchanges.listを組み合わせると、通知を取りこぼした場合にも同じ変更フィードから再同期できます。

ここでいうカーソルは、changes.getStartPageTokenまたはchanges.listの応答から得るページトークンです。これはアプリ側が「どこまで変更を確実に反映したか」を表す状態として扱います。Webhookのメッセージ番号や到着時刻を変更カーソルに代用してはいけません。Driveの通知には変更ファイルの一覧や本文、フォルダパスは含まれず、changes.listなどを別途呼び出して詳細を取得する必要があります。

初回同期で作る基準点

初回に現在の状態を同期する場合は、Drive APIから開始ページトークンを取得し、どの時点から差分取得を始めるか決めます。既存ファイルを全件列挙して初期データを作る設計と、現在以降の変更のみ追う設計は用途が違います。以前の状態を同期したいのに現在のstart tokenだけを保存すると、それより前のファイルは変更フィードから復元できません。初回に必要な全件列挙と、その後の差分同期を分けて設計してください。

実運用では、次の状態を同じデータストアへ保存します。

  • Driveのユーザーまたは接続アカウント識別子

  • 現在処理済みのpage token

  • 同期処理の開始・完了時刻

  • 最後に成功したページ数と取得件数

  • watch channelのID、resource ID、expiration

  • 再試行中のジョブと、最後に確定したカーソル

トークンは同期位置を示す状態です。アクセストークンやrefresh tokenとは別物ですが、内部実装情報として保護し、ログへ無制限に出さないでください。アカウント別の同期位置を混ぜると、別アカウントの変更を誤って既読にする可能性があります。

Watchを開始して期限を管理する

watch requestでは、新しいchannelごとに一意のIDとHTTPS callback URLを指定します。Drive API公式ガイドでは、有効な信頼済みSSL証明書が必要です。channel tokenを設定すると通知のX-Goog-Channel-Tokenヘッダーに返り、受信channelとの照合に利用できます。これは受信処理の補助情報です。OAuth bearer tokenなどの機密値をchannel tokenへ入れず、token照合だけを認証や署名検証の代替にしないでください。

expirationを指定しない場合の既定値は1時間です。公式ガイドが示す上限はfiles resourceが1日、changes resourceが1週間で、要求したexpirationよりAPI側の制約が短い場合は短い方が適用されます。応答にあるexpirationを保存し、固定の「毎週実行」だけに頼らず、実際の期限より前に更新ジョブを動かします。期限を超えたまま通知を待っても再開しないため、監視と再同期の両方が必要です。

channelは自動更新されません。更新時には新しい一意IDでwatchを作り、応答からchannel ID、resource ID、expirationを保存します。新旧channelが同じresourceで重複して動く時間があり得るので、その間の通知は重複として安全に処理します。新channelが作成できたことを確認してから旧channelを停止する順にすれば、期限更新時の空白時間を短くできます。新規watchの作成に失敗した場合、旧channelを早まって止めず、期限と再試行を監視します。

Webhookは短く受け、変更処理を別ジョブへ渡す

callbackはHTTPS POSTで呼ばれます。通知本文は空であり、ヘッダーにはchannel ID、resource ID、resource state、message numberなどが含まれます。sync通知はwatch作成時に届く初期通知で、watch応答より先に到着することもあります。通知は「変更があった可能性を確認する合図」として受け、到着順に変更内容を推定しないでください。message numberは増加しますが連番ではないため、欠番を再送漏れと断定できません。

Webhookの処理を重くして応答を遅らせると、受信側のタイムアウトや再送処理を難しくします。受信時には、channel IDとresource IDが登録済みの組み合わせか、期待するtokenか、resource stateが既知かを確認し、イベントを重複排除可能なキューへ入れ、成功応答を返します。Drive公式ガイドは200、201、202、204、102を成功応答として示しています。Google API Client Libraryを使う場合、500、502、503、504の応答では指数バックオフで再試行されますが、push通知だけを唯一の回復経路にしないでください。

不正なヘッダーや未知のstateが来ても、状態を無条件に更新しないようにします。一方で、未知のresource stateをすべて致命エラーにすると将来追加された通知で処理が詰まります。登録済みchannelの照合に失敗した通知は変更ログ処理へ進めず記録し、既知channelの想定外stateは安全に無視または隔離したうえで、定期的なchanges.list同期へ委ねます。

changes.listのページングとカーソル確定

通知を受けたワーカーは、保存済みのpage tokenを使ってchanges.listを呼びます。結果にnextPageTokenがある間は、次のページを取得します。最終ページでnewStartPageTokenが返ったら、それが次回ポーリング用の位置です。Google公式ガイドの例も、全ページを処理して最後に得たnewStartPageTokenを保存する形です。1ページ目を取得した時点や、通知を受け取った時点でカーソルを進めるのは避けます。

安全な処理単位は「取得した変更を適用し、その変更に対応するカーソルを確定する」ことです。業務データの更新とカーソル保存を同じDBトランザクションへ入れられるなら一緒にコミットします。そうできない場合は、変更IDやfile IDを使って適用処理を冪等にし、途中失敗時に同じページを再取得しても二重計上や破壊が起きないようにします。ページ途中で失敗したら、最後に確実に完了した位置から再実行し、全ページ処理が終わるまでnewStartPageTokenへ切り替えません。

たとえば3ページ目の処理でDB接続が切れた場合、1・2ページ目の反映と3ページ目の反映状態を区別できるようにします。1・2ページ目の変更を冪等に再適用できるなら、保存済みの旧カーソルから一括再取得する設計も可能です。API応答を受け取ったことと、自社DBへ反映できたことは別の状態として記録してください。ログには同期実行ID、開始カーソル、ページ番号、対象件数、コミット結果、次カーソルを残し、ファイル名や本文など必要以上の個人データは記録しません。

取りこぼしを検出する再照合

Push通知は即時性を高めますが、通知が届かなかったときに変更ログの確認を不要にするものではありません。一定間隔の定期同期を併用し、保存カーソルからchanges.listを再実行します。watchが有効でも定期同期を行えば、長時間停止、callback側の障害、更新処理の失敗後に差分を回収できます。定期実行の間隔は、Drive APIの利用量、必要な鮮度、システム停止時に許容できる遅れから決めます。公式仕様にない「必ず何分以内に届く」という保証を前提にしないことが大切です。

同期後には、取得件数・更新件数・削除または共有解除の扱い・最終カーソル・最後の成功時刻を監視します。通知が来ない時間があるだけで障害とは限りませんが、watch期限を過ぎている、定期同期も失敗している、最後の成功時刻が運用上の許容範囲を超えた場合は調査対象です。運用画面では「channelは有効」「変更feedの最終完了時刻」「未処理ページ」「再試行回数」を分けて表示すると、watch障害と同期ワーカー障害を切り分けやすくなります。

導入前の確認表

表は横にスクロールできます

確認箇所実施内容完了条件
初回基準点getStartPageTokenで同期位置を取得し保存アカウントとカーソルの対応が明確
CallbackHTTPS、証明書、channel IDとresource IDを照合正規channel以外の入力で状態を進めない
初期syncX-Goog-Resource-Stateがsyncの通知を処理watch応答前に来ても安全
変更取得保存カーソルからchanges.listを全ページ取得nextPageTokenがなくなるまで処理
反映変更適用とカーソル確定の順序を設計途中失敗後に重複適用しても壊れない
Watch更新channel IDとexpirationを保存し新channelを作成旧channel停止前に新channelを確認
回復定期的に同じ変更feedを再照合Push停止後も遅れて差分を回収できる
監視同期成功時刻・ページ数・遅延を監視通知とデータ反映の両方を追跡

失敗パターン別の切り分け

通知を受け取っているのにデータが更新されない場合は、callbackの成功応答だけでなく、後続ワーカーが起動したか、対象アカウントの保存カーソルを読めているか、changes.listの全ページを処理したかを確認します。ログ上でページ取得は成功していても、DB反映がロールバックされている場合があります。受信ログ、queue投入、API取得、DB commitを同じ同期実行IDで追えるようにしてください。

更新が途中から止まった場合は、まずchannel expirationと更新ジョブの実行履歴を見ます。期限切れなら新channelを作るだけで過去の未処理差分が自動で適用済みになるわけではありません。保存済みのchanges cursorからchanges.listを再開し、最終ページまで追い付いてから新しいカーソルを保存します。保存カーソルがない、または変更処理との対応が失われている場合は、既知の状態とDriveの変更フィードを照合し、必要に応じてアプリ側データを再構築する判断が必要です。

同じ変更が二重に反映される場合は、pushの重複だけを疑わず、チャンネル重複期間、通知の再試行、ワーカーの再実行を確認します。通知は合図として重複許容し、最終状態をchanges feedから取得する設計なら、同じ通知を複数回受けても同期結果を重複加算しません。ファイルに対する操作が「現在状態へのupsert」なのか「イベントを一回だけ加算する処理」なのかも明確にします。

共有解除・削除の扱いも事前に決めてください。changes.listの設定によって削除済み項目が結果に含まれるかが変わるため、自社側で単純に見えなくなったファイルを削除とみなすのではなく、APIのchange情報と対象範囲を確認します。共有ドライブを扱うなら、マイドライブのみを前提にせず、対象driveと検索条件が初期同期・差分同期で一致しているか確認してください。

実装前の安全な検証手順

本番データへ接続する前に、テスト用Driveまたは許可された検証環境で、通知の重複、sync通知の先着、変更ページの途中失敗、channel更新の重複期間、定期同期による再回収を模擬します。テスト用ファイルを作成・更新・削除し、Drive上の現状と自社DBの現状が最終的に一致するか確認します。ここでいうテストは設計上の例です。この記事の執筆時点では、特定の環境へ実際に接続したり、APIリクエストを実行したりしていません。

本番へ導入する場合は、最初にread-onlyの変更取得とログだけを有効にし、実際のデータ反映は差分レビュー後に段階的に有効化します。同期対象のdrive、ユーザー、共有ドライブ、削除状態、アクセス権の変更範囲を明確にし、意図しない対象まで取り込んでいないことを監視します。Drive APIの権限スコープとアプリ内のデータ閲覧権限は別に確認します。取得できる情報が増えるからといって、必要以上に広いOAuth権限を要求しないようにします。

変更通知同期の最小状態機械

実装時は、通知を受けた状態と変更適用済み状態を分けます。たとえば「通知受信」「同期ジョブ登録」「ページ取得中」「DB反映中」「カーソル確定」「完了」を別々に記録します。通知受信だけで完了にしないこと、ジョブが重複しても二つの処理が同じカーソルを競合更新しないことがポイントです。同一アカウントの同期を直列化するか、カーソル更新に楽観ロックを使い、古いジョブが新しいカーソルを巻き戻せないようにします。

変更feedの読み取りと自社DBへの反映を分離する場合は、ページごとに処理結果を記録するか、変更データを一時領域へ保持してからまとめて適用します。どちらがよいかは、変更件数、処理時間、DBのトランザクション制約で変わります。全てを一度にメモリへ抱えると大きな差分で失敗しやすく、ページ単位でカーソルだけ進めると途中失敗時の復元が難しくなります。先に「再実行できる最小単位」を設計し、その単位に合わせてログと保存位置を決めてください。

更新の順番は、API応答を受け取る、変更を正規化する、対象の現在状態を必要に応じてfiles.getで読む、自社データを反映する、反映結果を確定する、最後のカーソルを保存する、の順で整理します。通知自体から現在状態を作らないことで、順不同の通知や重複通知が来ても最終状態へ収束しやすくなります。変更フィードの項目にはファイルが見つからない状態もあり得るため、アクセス権や削除を含む状態を、単なる一時エラーと区別します。

Renewal Checklist

表は横にスクロールできます

タイミング操作保存するもの失敗時の対応
初回現在の変更開始位置を取得し、watchを作成page token、channel ID、resource ID、expirationwatchを作れない原因を解消し、カーソルから同期
定期点検expirationと同期最終成功時刻を確認現在channelと監視対象期限間近なら更新ジョブを早める
更新新IDでwatchを作り、応答を保存新旧channelと期限旧channelを維持し再試行
overlap旧・新の通知を同じ同期ジョブへ集約idempotency keyと処理状態重複は変更ログの読み直しで吸収
追いつき確認保存cursorから全ページchanges.listnewStartPageToken全ページ処理が完了するまでcursorを進めない
整理新channelと同期の健全性を確認して旧channel停止停止結果、時刻旧channel停止に失敗しても更新済channelを維持

実装を始める前のDecision Rule

まず、同期対象がファイル一つなのか、ユーザーの変更ログ全体なのかを決めます。前者ではfiles.watchの通知を受けてfiles.get等で状態を読み直す形が考えられます。複数ファイルをまとめて同期するならchanges.watchを通知トリガーにし、changes.listのカーソルを継続管理する方式が適しています。どちらも通知だけで差分内容が届くわけではありません。

次に、システム停止中に許容できる遅れと、変更feedを再照合する間隔を決めます。即時通知が業務要件でも、定期再照合をなくす根拠にはなりません。通知経路の監視、watchの有効期限、同期ワーカーの最終成功を別々に見ます。期限が短いfiles channelを選ぶ場合は、1日以内の更新ができない運用があるか検討してください。changes channelの最大期限は一週間ですが、上限まで毎回待つのではなく、実際のexpirationを読み、余裕を持って更新します。

最後に、変更の反映先が「現在状態を同期するテーブル」か「変更イベントを履歴として記録するテーブル」かを確定します。現在状態の同期ならファイルIDをキーにupsertし、削除やアクセス権変更の扱いを設計します。履歴ならmessage numberや通知回数を変更そのものの件数とみなさず、changes feedの各変更を一意に追跡する方法が必要です。要件が曖昧なままでは重複通知を受けたときの期待結果が決まらないため、対象範囲、保持期間、削除の扱いを実装前に決めます。

実装前チェックリスト

  • 初回の全件同期と差分同期を区別した

  • アカウントごとにstart tokenと継続cursorを保存する

  • Watchの実expirationを保存し、期限前の更新を監視する

  • 新channelを確立してから旧channelを停止する

  • Webhookはchannel ID / resource ID / tokenを照合して短く応答する

  • 通知を重複して受けても、同期状態が二重計上されない

  • changes.listの全ページを取得してから最終cursorを確定する

  • API取得成功とDB反映成功を別々に記録する

  • 定期同期でpushの欠落や処理失敗を再照合する

  • 未知のresource stateや権限変化を安全に記録・処理する

  • API制限、OAuth権限、共有ドライブの対象範囲を検証環境で確認する

  • 本番データへ適用する前に、読み取り結果と期待対象を照合する

AIものさしの判断

Drive push通知は同期の起点を早めますが、変更データそのものではありません。複数ファイルの同期を継続するなら、通知は「変更フィードを確認するsignal」として受け、保存カーソルからchanges.listを全ページ読み、適用後にだけカーソルを進める設計にします。

実装では、watch channelの期限管理、Webhookの照合、changes.listのページング、冪等なDB反映、定期再照合の五つを別々に設計してください。初めて組む場合は、まずテスト環境でread-only同期を動かし、変更ログとローカルの対象状態が一致することを確認します。削除・共有解除の扱い、複数アカウント、共有ドライブの範囲が決まっていない場合は、その条件を先に決めてから本番連携へ進みます。

このDraftではGoogle Drive APIへの実リクエストやWebhookの実装テストをしていません。ここにある状態機械と検証項目は設計例であり、特定のSDK、OAuth scope、DB、運用環境での動作保証ではありません。導入時には利用中のAPI version、必要なscope、共有ドライブ設定、quota、削除方針を実際の環境で確認してください。

Sources & scope

確認日:2026-10-15

確認範囲:watch可能なresource、HTTPS callback、channel ID/token/expiration、通知ヘッダーと空body、通知から変更詳細を取得する方法、getStartPageToken / changes.listのページング、newStartPageTokenの保存位置。実環境のAPI呼出し、quota、OAuth scopeの最小構成、アカウント固有の共有ドライブ動作は未検証です。