いいか、よく聞け。現場のエンジニアが陥りがちな最大の勘違いは、「監査ログなんて、とりあえず標準機能のスイッチを入れれば終わりだ」という甘い考えだ。
Cloud Spannerという、TrueTimeとPaxosの上に築かれた「分散データベースの極北」を扱うなら、その甘さは命取りになる。ミッションクリティカルなシステムにおいて、「トランザクションは成功したが、監査ログが残っていない」あるいは「監査ログを出力したせいで、スループットが半分になった」という事態は、設計者の敗北を意味する。
今日は、Spannerの内部構造から紐解く、監査ログの整合性とレイテンシ保証の「極限の設計」について講義する。
—
1. 監査ログの「所在」を峻別せよ
Spannerにおける監査ログには、大きく分けて2つのレイヤーがある。これを混同しているうちは三流だ。
1. Cloud Audit Logs (Data Access Logs): Google Cloudのインフラ層が吐き出すログ。
2. Application-level Audit Logs: ビジネスロジックとして定義し、Spannerのテーブルに書き込むログ。
今回のテーマである「トランザクションとの整合性」において、真に議論すべきは後者、および前者の「遅延の正体」だ。
2. Cloud Audit Logsの真実:非同期の「壁」
Google Cloudの標準監査ログ(Data Access Logs)は、SpannerのAPI実行に対して「ベストエフォートかつ、ほぼリアルタイム」で生成される。しかし、ここで理解すべきは「Paxosのコミットとログの生成はアトミックではない」という事実だ。
Spannerがクライアントに「Commit成功」を返した瞬間、ログはまだCloud Loggingのバッファの中にいる。この数秒のタイムラグは、金融系のコンプライアンス要件では「監査ログの欠落」と見なされるリスクがある。
- アーキテクトの知見:
「APIを叩いた事実」を残すだけならこれでいい。だが、「何がどう変わったか(Before/After)」を厳密に、かつトランザクションと不可分に記録したいなら、標準ログに頼るのは素人だ。
3. Change Streams:Spannerが到達した「第三の道」
整合性とパフォーマンスを両立させるための最強の武器、それが Change Streams だ。
Change Streamsは、Spannerの内部的なミューテーション(変更)をキャプチャする仕組みだが、これはPaxosグループのログレプリケーションと密結合している。 つまり、データ本体の書き込みと変更ログの生成は、Spanner内部で極めて高い親和性を持って処理される。
Change Streamsを利用した堅牢なアーキテクチャ
— 監査対象のテーブルにChange Streamを設定する
CREATE CHANGE STREAM AuditLogStream
FOR ALL; — もしくは特定のテーブル、カラムに限定
このストリームをDataflowで拾い、BigQueryや別のSpannerインスタンスに流し込む。
- 利点: トランザクション本体のレイテンシにほぼ影響を与えない(非同期抽出)。
- 整合性: Spannerのコミットログから生成されるため、トランザクションが成功してログが残らないという事態は、Spanner自体の崩壊を意味する。つまり、理論上100%の整合性が担保される。
4. 究極の整合性:トランザクション内「ダブル・ライト」の罠と攻略
「監査ログも同一トランザクションで同じテーブル(あるいは別テーブル)に書き込みたい」という要求は、要件定義で必ず出てくる。だが、Spannerでこれを愚直にやると、パフォーマンスが死ぬ。
避けるべきアンチパターン:シーケンシャルなログテーブル
— アンチパターン:挿入順に並ぶログテーブル
CREATE TABLE AuditLogs (
LogId STRING(36) NOT NULL,
CreatedAt TIMESTAMP NOT NULL, — ここがホットスポットになる!
…
) PRIMARY KEY (CreatedAt, LogId);
Spannerはレンジパーティショニングを行う。`CreatedAt`を先頭キーにすると、書き込みが特定のサーバー(スプリット)に集中し、システム全体がスローダウンする。
プロの設計:UUID v4 + 追記型設計
監査ログを同一トランザクションで書き込むなら、以下の3点を死守せよ。
1. Primary Keyの分散: UUID v4を使用し、書き込みを全ノードに分散させる。
2. Mutation Countの意識: Spannerには1トランザクションあたりのミューテーション制限(80,000)がある。ログを細かく書きすぎると、本体のバルク更新を圧迫する。
3. インターリーブの活用: 親テーブルの変更ログなら、親テーブルに `INTERLEAVE IN PARENT` させることで、コロケーション(物理的に同じ場所に配置)を強制し、分散トランザクションのオーバーヘッドを最小化する。
5. レイテンシの正体:TrueTimeがもたらす「待機」
Spannerのコミットレイテンシには、`TrueTime.now()` の不確実性($\epsilon$)を解消するための「コミット待機時間」が含まれる。
監査ログをトランザクションに含めると、書き込むデータ量(Mutation)が増える。これは、Paxosの合意形成に参加するフォロワーへの転送量が増えることを意味する。
- 知見: Spannerのレイテンシを決定するのは「距離」と「ミューテーションサイズ」だ。
- 対策: 監査ログに「変更前の値(Old Value)」を無理に詰め込むな。それはChange Streamsに任せろ。トランザクション内では「誰が(Actor)」「何を(Action)」したかという最小限のメタデータに絞れ。
—
結論:テクニカルリードとしての審判
もし君が「監査ログの整合性とレイテンシ」についてレビューを求められたら、こう断言してほしい。
1. 「法的強制力のある証跡」が必要なら、Change Streamsを採用せよ。 これが最もスケーラブルで、Spannerのアーキテクチャに即した正解だ。
2. 「ビジネスロジックとしての即時参照」が必要なログのみ、同一トランザクションで書き込め。 その際は必ずPKを分散させ、ホットスポットを回避すること。
3. Cloud Audit Logsは、あくまで「誰がAPIを叩いたか」という監査の補助として使い、データの整合性担保には使うな。
Spannerは魔法ではない。分散システムの物理法則に従っている。その法則を理解し、Paxosの波を乗りこなす設計をすること。それが、世界最高峰のエンジニアに求められる職人芸だ。
以上だ。次の設計レビューまでに、Change Streamsの保持期間(Retention period)とコスト計算を済ませておけよ。
コメント