本文へ移動

2026-05-01 時点の資料です。読みやすさのために、一部の文字の色を濃くしています。

アーキテクチャ/ Runtime topology & data flow

松葉 / Matsuba の実行時の全体像と、主要な業務シーケンスを図解します。Cloud Run(コンテナを HTTP リクエストごとに起動するサーバレス基盤)上に 4 つのサービスと 1 つの Jobs を分けてデプロイし、5 つの Cloud Storage バケットと Cloud Pub/Sub(Google Cloud の非同期メッセージング基盤)を組み合わせてイベント駆動で連携させています。

01

全体構成Runtime topology

ユーザのリクエストは Cloud Load Balancer(経路制御)と Cloud Armor(WAF と IP レート制限)を経由して Cloud Run サービスに届きます。データは Supabase Postgres(DB のみ利用)と Cloud Storage に保存し、サービス間の非同期連携は Cloud Pub/Sub で行います。すべて東京リージョン(asia-northeast1)に配置します。

図 01 · ランタイムトポロジ
社内ユーザGoogle SSO · 社内ドメイン社外ユーザメール + パス · OTP MFA 必須Cloud Load Balancer + Cloud ArmorTLS 終端 · WAF · IP レート制限 · DDoS 防御CLOUD RUN · TOKYOdocshare-webNext.js 16 · App Router常時稼働 · 上限ありdocshare-apiHono on Node.js 22常時稼働 · 上限ありdocshare-syncGo · Drive 同期ワーカー需要に応じて縮退 · 上限ありdocshare-scannerGo + ClamAV 1.4常時稼働 · 上限ありdocshare-batchCloud Run Jobs18 種 / SchedulerRPCINFRASTRUCTURESupabase PostgresDB のみ · RLS · PITRCloud Storage5 バケット分離 · ResumableCloud Pub/Sub12 topics · DLQ · 冪等性EventarcGCS → ScannerResend招待 · 通知 · OTP共有ドライブ · 社内アーカイブBox 単位の片方向ミラー · appProperties で突合PushDrive API · 片方向Scheduler 経由で 18 種のバッチを起動

読み方: Teal の実線は同期的な主経路、灰色の細線は非同期またはバッチ経由、点線はスケジュール起動を示します。docshare-web と docshare-api は常時稼働、docshare-sync は需要に応じて縮退します。

02

4 サービス + 1 ジョブの責務Service responsibilities

サービスはそれぞれ単一の関心事に集中させ、互いに HTTP / Pub/Sub / Eventarc を介して連携します。1 つのサービスの不具合が別のサービスに連鎖しにくい構成です。

サービス採用技術主な責務インスタンス
docshare-webNext.js 16 · React 19 · TypeScript画面表示と Server Components。proxy.ts(旧 middleware.ts)で Better Auth のセッション検証を行い、変更操作は API へ委譲します。常時稼働 · 上限あり
docshare-apiHono 4 · Node.js 22 · Prisma 7REST と Hono RPC、Better Auth、署名 URL 発行、Cloud Tasks 経由の通知ワーカーを同居させた中核 API。常時稼働 · 上限あり
docshare-syncGo 1.23+ · Pub/Sub PushDrive 同期ワーカー。file.scan_clean イベントを購読し、Google Drive へ片方向で同期します。appProperties で突合します。需要に応じて縮退 · 上限あり
docshare-scannerGo + ClamAV 1.4Eventarc で 未検査バケットの finalize 通知を受け、ClamAV でスキャンし、検査済みバケットまたは 隔離バケットへ書き換えます。常時稼働 · 上限あり
docshare-batchCloud Run Jobs · Cloud Scheduler定期バッチ 18 種を --args でジョブ種別に振り分け。retention 削除 / Drive 補修 / JWKS ローテ / EICAR テスト / admin メトリクス集計など。スケジューラ起動

分離デプロイの利点: サービスごとに独立してリリースできるため、ウイルススキャナの ClamAV シグネチャ更新と Web 画面の改修が衝突しません。docshare-batch も同一のコンテナイメージから生成するため、ライブラリのバージョン差は発生しません。

03

5 バケット構成Five-bucket storage

Cloud Storage は用途別に 5 つに分けます。アップロード直後のファイルと、ウイルススキャンを通過した利用可能ファイルを物理的に異なるバケットに置くことで、未スキャンファイルが業務動線に紛れ込むことを構造的に防ぎます。

バケット用途ライフサイクル
未検査バケットUL 直送先。クライアントから Resumable Upload で書き込み、Eventarc 通知でスキャンへ。一定期間で自動削除(スキャン完了済みのため不要)
検査済みバケットスキャン通過済みの利用可能ファイル。署名 URL は本バケットからのみ発行します。Object Versioning ON · noncurrent は一定期間で削除
隔離バケット感染検出・拒否ファイルの隔離。専用 SA でのみ取り出し可能。一定期間で自動消去候補(手動承認制)
ミラーバケット(ウイルス定義)ClamAV シグネチャの社内ミラー。freshclam が 6 時間ごとに更新します。一定の世代を保持、不正検出が続くと system_admin 通知
共有 ZIP バケット共有リンク・一括 DL の ZIP 一時配信用。受取人のアクセスごとに署名 URL を発行します。一定期間で自動削除
04

主要シーケンスCore sequences

業務上もっとも頻度の高い「ファイルのアップロードからスキャン・Drive 同期までの 9 ステップ」と、長期運用を支える「保持期間による自動削除の 7 ステップ」を縦並びで図解します。

図 02 · アップロード → ウイルススキャン → Drive 同期
① クライアント検証拡張子・MIME・マジックバイト・サイズ 200 MB チェック② docshare-api が署名 URL を発行UL 用 V4 署名 URL · 一定時間で失効③ 未検査バケットへ Resumable 直送tus-js-client · 再開可能 · 200 MB まで④ Eventarc が finalize イベントを PushOIDC 認証付き · 配送遅延 P95 < 5 秒⑤ docshare-scanner が ClamAV でスキャン100 MB なら P95 < 15 秒⑥ 検査済み / 隔離バケットへ書き換えscan_status 更新 · UI バッジが pending → clean に⑦ Pub/Sub に file.scan_clean を publishevent_id (UUIDv7) · processed_events で冪等⑧ docshare-sync (Go) が購読同期 ON の Box のみ · 指数バックオフで再試行 + DLQ⑨ Drive へ Resumable UploadappProperties.docshare_* で突合キー付与ClientAPIGCSEventarcScannerGCSPub/SubSyncDrive

ポイント: ファイルが UI 上で利用可能になるのは ⑥ の scan_status=clean 反映後です。それ以前はアップロード者にしか「スキャン中」表示されません。Drive 同期は同期 ON の Box でのみ走り、失敗時は上限回数まで指数バックオフでリトライし、それでも失敗した場合は DLQ(Dead Letter Queue · 失敗メッセージの隔離先)に送られます。

図 03 · 保持期間による自動削除フロー
① Cloud Scheduler が起動日次② retention-deletion-daily Jobdocshare-batch を args=retention-deletion-daily で起動③ 削除対象を DB から SELECTretention_expires_at < now() AND hold = false④ 論理削除 + 監査ログ記録file.delete_retention · outbox_events に file.deleted_soft⑤ Resend で当日通知メール送信Owner + UL者 + 最終アクセス者 · immediate 強制⑥ 30 日後 trash-cleanup-daily日次 · 物理削除候補を抽出⑦ GCS から物理削除 + Drive ゴミ箱送りdrive.files.update trashed=true · drive_archive_files へ転写30 日のクッションSchedulerCloud Run JobsPostgresPostgresResendCloud Run JobsGCS + Drive

2 段階削除の意図: ④ の論理削除と ⑦ の物理削除の間に 30 日のクッションを置き、当日通知メール(⑤)で利用者に気づきの機会を与えます。重要なファイルは「Hold(リーガルホールド)」フラグで自動削除の対象外にできます。Hold 中は本フローの抽出条件 hold = false でスキップされます。

05

信頼性パターンReliability patterns

イベント駆動のシステムで起こりがちな「DB 書き込みは成功したが Pub/Sub publish に失敗した」「同じイベントを 2 回処理してデータが二重になった」「リトライが終わらず復旧できない」を、3 つのパターンで構造的に防いでいます。

Outbox Pattern

DB と Pub/Sub の整合性

UseCase で DB 書き込みと同時に outbox_events テーブルへ INSERT します。1 分間隔の outbox-dispatcher が未送信レコードを Pub/Sub に publish するため、「DB は更新できたが publish に失敗」の状態が発生しません。

Idempotency Key

冪等性で二重処理を防ぐ

各イベントには UUIDv7 形式の event_id を付与し、購読側は processed_events テーブルで重複を弾きます。同じイベントを 2 回処理しても結果が変わらないため、Pub/Sub の at-least-once 配送と整合します。

DLQ + Backoff

失敗を捕まえて運用に渡す

購読側で例外が起きると、Pub/Sub が指数バックオフで上限回数までリトライします。それでも失敗した場合は Dead Letter Queue に隔離され、/admin/sync 画面から可視化・手動再投入できます。失敗を見逃さない仕組みです。