Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Spanner Change StreamによるTransactional Outboxでの ...

Avatar for Satoshi Amemiya Satoshi Amemiya
October 14, 2025
95

Spanner Change StreamによるTransactional Outboxでの 分散トランザクションの高速化

Avatar for Satoshi Amemiya

Satoshi Amemiya

October 14, 2025

Transcript

  1. Goでマイクロサービスをやる - gRPCでBFF/サービス間の通信 - gokitをベースに⾃社独⾃の共通ライブラリをメンテナンス - ドメイン単位でマイクロサービスがk8sに展開 - データベースにはSpannerを採⽤ -

    全IPがインスタンスに同居している - IP毎のTenantIDを各テーブルのPKとして持っているので - マルチテナンシーを実現しつつ、⾃然に分散する感じ - インデックスだったり分散のためのキーだったりで普通のRDBMSと差はある - が、今回の話だと割と普通のトランザクショナルなデータベースとして捉えてもら えれば⼤丈夫です - コストは思われている程ではないのと、将来のスケールコストを先払いしているという 理解でいる - 最近だとtidb、Supabaseとか、pgsql本体の進化が強くて個⼈的な気持ちは揺らい でいるが……
  2. マイクロサービスのおさらい Pros - スケールさせやすい - パフォーマンスに問題のあるサービ スを個別でスケールアップ/スケール アウトできる - 影響範囲をチームの単位で切りやす

    い - コンウェイ/逆コンウェイ的な - (私たちのような)マルチテナントと の相性がよい - テナント毎に、提供したい機能で必 要なマイクロサービスを組み合わせ る Cons - サービス間通信でのデータの⼀貫性 - 疎結合が故のやつ - gRPCやOpenAPIのようなスキーマ 駆動である程度対策ができる - ランニングコスト - 私たちで⾔えば、スケールに伴うコ ストを先払いしている形 - サービス間で⼀貫したトランザク ションを実現しにくい - ↑これ!
  3. Saga vs Outbox - アプリケーションの処理フローとして、「失敗しうる」処理かどうか - 外部サービスへの依存の有無 - 処理が失敗した場合のハンドリングをOutboxで⾏おうとするとちょっと⾯倒 -

    PubSubのリトライ戦略やデッドレターの設計が必要になる - 「確実に成功させたい」処理 -> Outbox - 「失敗も含め結果整合性を得たい」-> Saga - フローの傾向として、Outboxの⽅がフィットするパターンは多い - アプリケーションの特性にもよるが - アプリ全体としてレジリエンシー重視をする判断 - 補償トランザクションの必要性を減らす - コードやコンポーネントの⾒通しがよくなる 決済システムでは、ステートマシンでSagaっぽく状態管理しつつ、他サービスとの協調はOutboxでやっている(余談)
  4. Spanner Change Stream - https://cloud.google.com/spanner/docs/change-streams?hl=ja - データベース全体や特定のテーブルに対し、指定した変更が発⽣したことを イベントとしてPublishする機能 - タイムスタンプを指定して、それ以降のイベントをストリームで受信できる

    - ただし、イベントの保持期間(7⽇)はある - 他のDBでも類似機能はあるはず - CDC、あるいはLISTEN / NOTIFY - 標準ではなくプラグインなら、SupabaseのRealtimeや、ETLはかなり近い
  5. Outbox with Change Stream - チームメンバーが書いた記事 - マイクロサービスの共通ライブラリ(gokit)に、OutboxへのPublishのための コンポーネントを実装 -

    ここで記事からコードサンプルを⾒せる - サービスのプロセス内に、Change Streamを購読するgoroutineを起動 - インスタンス間でのAtomic性のために、ロックを取りつつPubSubへPublish
  6. 運⽤の上での知⾒ - 結果整合性が得られるまでの時間が5s -> 200msに 🎉 - イベント単位での再送機構はない - 「タイムスタンプを指定して、それ以降のイベントをストリームで受信できる」

    - 従来のPollingの機構を、頻度を下げつつ継続運⽤している - サービスメトリクスが⼀⾒変な内容になる - 「クエリが⻑時間実⾏され続けている」みたいな扱いになる - 監視の調整をすればよい話ではありつつ - 新しめの機能で、公式SDKでのコネクションの扱いが不安定だったりした - 急にSpannerインスタンス側からコネクションがDropされたりする - フォールバック機構を⼊れつつ、サポート依頼を投げたり - Subscriberをサービスプロセスに同居させるかは議論の余地がある - k8sを採⽤しているので、Outbox Sidecarとしてインスタンス分離した⽅がサービスレ ベルは上げられそう
  7. まとめ - 分散トランザクションは難しいが、やる価値がある - マイクロサービスの⼤きな弱点の⼀つ - だからこそ、ここに決まったパターンを設定できると⼤きい - マイクロサービスのメリットを純粋に享受できる -

    普段の普通の機能を作るときに意識することが減り、開発の速が上げられる - Goで作っていると、マイクロサービスの基盤に⼿を⼊れやすい - 今回みたいなアイデアを、軽量にプロセス内で試せる - ⾮同期プログラミングの強⼒さと、システムプログラミングの武器が揃っている