Pub/Sub + Eventarc + Cloud TasksAsync messaging backbone
松葉のサービス同士を疎結合に繋ぐ「非同期メッセージング基盤」を整備しました。Pub/Sub はクラウド版の「掲示板+購読者」、Eventarc は「ストレージの変更を即座にスキャナに伝える専用線」、Cloud Tasks は「メール送信をキュー越しに非同期化する HTTP ジョブ」です。
本手順で作成したものの規模感By the numbers
業務イベント用と、配信失敗の退避先 DLQ を 1 対 1 で用意。
主購読 9(Push + OIDC)+ DLQ 監視用 13(Pull)。
未検査バケットの変更 → scanner を起動。
notification-queue(メール送信ジョブの非同期化)。
業務イベント Topic 一覧 · 13 種Business topics
ファイル / フォルダ / 共有リンク / Box の状態変化を、それぞれ独立した topic に publish します。受信側は必要な topic だけ subscribe するので、サービス追加・削除が他サービスに影響しません。
| Topic | 発火元 | 購読者の業務内容 |
|---|---|---|
file.uploaded | matsuba-api | アップロード完了(トレース用、subscriber 無し) |
file.scan_clean | matsuba-scanner | matsuba-sync が Drive 同期を起動 |
file.scan_infected | matsuba-scanner | matsuba-api が感染通知メールを送る |
file.replaced | matsuba-api | Drive 側を新世代に更新 |
file.deleted_soft / folder.deleted_soft | matsuba-api | 論理削除(30 日後の物理削除まで no-op) |
file.deleted_hard / folder.deleted_hard | matsuba-batch | Drive ゴミ箱に送る |
folder.created / folder.renamed | matsuba-api | Drive 側にフォルダ作成 / 改名を反映 |
share.created | matsuba-api | 受取人にメール送付 |
share.accessed | matsuba-api | 監査用(subscriber 無し、publish のみで audit_logs に既に記録済) |
box.drive_sync_toggled | matsuba-api | Drive 同期 ON/OFF の切替 |
配信失敗を取りこぼさない DLQ 設計Dead Letter Queue
Pub/Sub は配信保証が「最低 1 回」のため、受信側で重複排除を実装します。一方、配信が 上限回数まで連続して失敗したメッセージは「DLQ(退避キュー)」に流して保持します。13 トピック全てに 1 対 1 で DLQ topic + 監視用 pull subscription を用意し、後で手動再投入できる構成にしました。
同じ構造を 13 セット用意: 上図は file.scan_clean 1 トピック分ですが、同じ構造を 13 種類全てに対して用意しています(主購読の有無は経路毎に違いますが、DLQ + DLQ pull subscription は 13 全件)。Pub/Sub のドキュメントが「DLQ topic を作っただけではメッセージが消失する」と明記しているため、必ず pull subscription で保全しています。
Eventarc トリガStorage to scanner
Eventarc は「クラウド内の出来事(イベント)を Cloud Run に Push する」専用の連携サービスです。本構築では 未検査バケットへのファイル追加を即座にスキャナ Cloud Run に通知する 1 本だけ作成しました。
| 項目 | 値 |
|---|---|
| トリガ名 | unscanned-finalize-to-scanner |
| 監視対象 | 未検査バケット |
| イベントタイプ | google.cloud.storage.object.v1.finalized(ファイル作成完了) |
| 宛先 | matsuba-scanner の /eventarc/finalized エンドポイント |
| SA | scanner の実行 SA |
| 配送遅延 SLO | P95 で数秒以内を目標(ファイル追加から数秒以内に scanner が起動) |
Cloud Tasks Queue notification-queueMail enqueue
通知メール送信は「画面操作に対する即時応答」よりも「確実に送り終える」を優先したいため、メール送信処理は Cloud Tasks キューに enqueue(仕事を並ばせる)し、matsuba-api 内のワーカールートが順次処理する設計です。Resend のレート制限を超えないよう、最大ディスパッチ数に上限を設定しました。
| 項目 | 値 |
|---|---|
| キュー ID | notification-queue |
| ロケーション | asia-northeast1 |
| 最大ディスパッチレート | 上限を設定(Resend のレート制限内) |
| 最大同時ディスパッチ数 | 上限を設定 |
| 最大試行回数 | 上限回数を設定 |
| 最大試行時間 | 上限時間を設定(超えたら失敗確定) |
| バックオフ | 指数バックオフ |
SA × Topic / Subscription への権限Per-resource IAM
Pub/Sub は トピック単位 / サブスクリプション単位で IAM 権限を付与します。プロジェクト全体に pubsub.publisher を付ける近道は取らず、各 topic ごとに「この SA だけが publish できる」を厳守しました。
| SA | 付与ロール | 対象 |
|---|---|---|
api の実行 SA | pubsub.publisher | 9 トピック(file.*, folder.*, share.*, box.*) |
scanner の実行 SA | pubsub.publisher | 2 トピック(file.scan_clean / file.scan_infected) |
batch(Jobs)の実行 SA | pubsub.publisher | 2 トピック(file.deleted_hard / folder.deleted_hard) |
sync の実行 SA | pubsub.subscriber | 7 サブスクリプション(sync 側 Push 経路) |
api の実行 SA | pubsub.subscriber | 2 サブスクリプション(通知系) |