はじめに
前回の記事では、Transactional Outbox Pattern の構築について紹介しました。
本記事では、運用開始前に行う準備、実際に運用してみて直面した課題について共有します。
TL;DR
- 運用開始前に、ローカルを介したCloud SQL Auth Proxyを用いて、それまでのデータを同期
- 本番運用すると差分同期失敗やイベント遅延など複数の課題が顕在化
- Transactional Outbox Patternによる差分同期失敗の暫定策として差分同期バッチ処理を追加
運用開始前に行う準備
ベースデータ同期
Transactional Outbox Pattern は差分同期の仕組みです。そのため、運用開始前にそれまでのデータを同期する必要がありました。
この作業は初回のみ必要であり、Cloud SQL Auth Proxy を用いて参照元ProjectのDBから psql経由でCSVをローカルPCにダウンロードし、参照先Projectに同じくpsqlで投入しました。
運用中に直面した課題
実際に運用を始めると、差分同期に失敗するケースが複数発生しました。
特に下図の 3 つの連携部分で問題が確認されました。
Backend Application -> Database(トランザクション不整合)
発生した問題
- Database のレコードは作成されるが、以下のエラーと共にOutbox Table にイベントレコードが作成されない
Transaction API error: Transaction already closed: A batch query cannot be executed on a committed transaction.
原因
- トランザクション実装が正しく機能しておらず、一部成功、一部失敗する状態となっている
対応
- トランザクションの処理を見直せば解決する可能性があるが、既存実装全体に関わる根深い問題であり、すぐには対応できなかった
- もし事前に分かっていれば、Transactional Outbox Pattern ではなく Change Data Capture を採用していたかもしれない
- Change Data CaptureはDatabaseのコミットログを用いるため、このトランザクション問題を回避できる
- 完全な解決には時間を要するため、下記の暫定対応を導入し手動でデータ同期を実施した
Message Broker -> Database Updater(Pub/Sub滞留)
発生した問題
- イベントが参照元Projectに届くまでに 1 日以上かかる
原因
- Database Updater で最大リクエスト数やスケールの制限がかかり、正常レスポンスを返せなかった
- Pub/Sub に 40 万件のメッセージが滞留しており、Pub/Subが受け取ったメッセージを送信するまでに時間がかかる状態になっていた(下図)
- Database Updaterがエラーレスポンスを多発させていたため、Pub/Sub 側で送信量制限がかかった
対応
- Cloud Run の最大同時リクエスト数を調整
- Cloud Run のスケールアウトを設定
- Pub/Sub に滞留していたメッセージをパージ
- エラーレスポンスを適切に制御
Database Updater -> Database(トランザクションタイムアウト)
発生した問題
- Database Updater が書き込みを行う際、以下のエラーと共に失敗する
Transaction API error: Unable to start a transaction in the given time.
原因
- 大量のイベントを一度に処理しようとした際、Prisma のトランザクションが待ち時間 (maxWait) を超えてタイムアウト
- Cloud SQL がリクエストを捌き切れず、コネクション確立に時間がかかることでエラーが連鎖
対応
- Prisma のトランザクション設定を見直し、待機時間やタイムアウトを緩和
- Database Updater 側のリクエストを最適化(まとめ書き込みやバッチ処理を導入)
- 完全な解決には時間を要するため、下記の暫定対応を導入し手動でデータ同期を実施した
暫定対応: 差分同期バッチの導入
上記の課題は根本解決に時間を要するため、Transactional Outbox Patternによる差分同期に加え、暫定的に 下図の定期的に差分を同期するバッチ処理 を構築することとしました。
データ参照先Projectの流れ
- Message RelayによるPollingを一定時間停止
- Message Brokerに溜まっているメッセージを破棄
- Cloud SQLに保存されているデータをCSVとして参照元のCloud Storageに保存
- 一定時間経過後にMessage RelayによるPollingを再開
データ参照元Projectの流れ
- 参照先のMessage RelayによるPollingが停止している間に、参照元のCloud Storageに保存されたデータを用いて差分の同期を行う
おわりに
外部システムとの連携は難しいですね!
特に、検証環境でうまくいっても本番環境ではデータ量やリクエスト数の違いから予期せぬ問題が顕在化したため、リクエスト量も含め検証環境で確かめておけばよかったと実感しています。
最後に、記事執筆にあたりご意見いただきました みつなが さんに深く感謝申し上げます。



