Debezium connector for MySQLで、outboxテーブルのデータを全送することに対処する方法
この記事は「Money Forward Engineers Advent Calendar 2024」の12月25日担当分のものになります。
はじめに
Debezium connector for MySQLを運用に載せている場合、GTIDを使用してDebezium起動時にそれまでに処理したところからデータの送信を再開させると思います。
ただ、稀にoutboxテーブルのデータを再送する、ということが発生します。
特にAurora MySQLで起きるんじゃないかなぁと思います。
これの対処方法と、何でそんなことが起きるのかについて書いて残しておくページです。
やっておくべきこと
とりあえずoutboxテーブルのデータで、Kafkaへ送信済みのメッセージをずっと持ち続けるということをやめましょう。
Debeziumを使用しているということは、マイクロサービス間のデータ連携などを行っているんじゃないかなぁと思います。(私はそう)
その場合、データを消してしまうとデータ連携先に対して何れかリカバリーをしないといけなくなった場面で対処が面倒になるため、5日以上前にINSERTされたデータを別テーブルに移動する(INSERTして、その対象を削除)というのがおすすめです。
コンシュームされたら消すみたいなモノでも良いですが、Debeziumではat least onceを基本としているのでKafka上からoutdatedされない範囲のデータを即座に対処する必要はない、というのが私の考えです。
こうしておくことで、outboxテーブルのデータは最小限にしておけます。
Outboxテーブルのデータを再送する時に、Debeziumは何をしているのか
Debeziumは起動時にOffset Storage用のKafka TOPICのデータを参照します。
このTOPICはbinlogファイル名やposition、GTIDの情報なんかが入っています。
参考:
[
"outbox-connector-name",
{
"server": "topic.prefix + database.server.name"
}
]
{
"transaction_id": null,
"ts_sec": 1735093896,
"file": "mysql-bin-changelog.000001",
"pos": 46471848,
"gtids": "4322be09-cd18-34c2-8e95-e3980e39f49b:1-3120,7fab7e99-582b-3986-ac1e-975b1851d81d:1-6565106,c9d78d93-2722-3826-af52-3b56c776d8eb:1-244993401",
"row": 1,
"server_id": 513294170,
"event": 8
}
この情報を元に続きから開始するのですが、Aurora MySQLだと割と使われなくてoutboxテーブルのデータを全送するというのが起きます。
Aurora MySQLだとなぜ全送するのか
GTIDを有効化する場合、binlogを有効にするというのはDebeziumのドキュメントにも書いてあります。
が、Auroraのbinlogは有効化したとしても速やかに消す、といった動きがデフォルトになっています。
これが意味するところは、土日などシステムがほぼ使われない期間があるとbinlogが消えて、
このタイミングでDebeziumが再起動すると、Offset Storage用のTOPICからデータを取得してきて続きから再開しようとするが、binlogが無くなっているため仕方なくoutboxテーブルのデータを全送する、ということが起きます。
上記リンクに書いてある通りでbinlog retention hours
を設定するのも有効ですが、おそらく万能ではないので、outboxテーブルのデータを退避してスリム化するというのが安牌です。
参考:Debeziumのコードの歩き方
outboxテーブルのデータを全送するという問題はステージングで遭遇して、理由がわからないからDebeziumのコードを読みまくったので、ついでに歩き方についても残しておきます。
Debeziumの起動に関しては、
です。
Debeziumを起動すると、上記メソッドがio.debezium.connector.common.BaseSourceTask#startを実行します。
この中から、io.debezium.connector.mysql.MySqlConnectorTask#start
を実行する構成です。
snapshotの処理もgtidから再開する処理もMySqlConnectorTask内でそのクラスをセットアップしてるので、このクラスを起点にじっくり読みましょう。
io.debezium.connector.mysql.MySqlConnectorIT
のテストをデバッグすると速く理解できます。
終わりに
Debezium connector for MySQLでoutboxテーブルのデータを全送することに対処する方法についてでした。
Discussion