😇

Debezium connector for MySQLで、outboxテーブルのデータを全送することに対処する方法

2024/12/26に公開

この記事は「Money Forward Engineers Advent Calendar 2024」の12月25日担当分のものになります。

https://adventar.org/calendars/9988

はじめに

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の情報なんかが入っています。

参考:

key
[
	"outbox-connector-name",
	{
		"server": "topic.prefix + database.server.name"
	}
]
value
{
	"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は有効化したとしても速やかに消す、といった動きがデフォルトになっています。
https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/mysql-stored-proc-configuring.html#mysql_rds_set_configuration-usage-notes.binlog-retention-hours

これが意味するところは、土日などシステムがほぼ使われない期間があると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テーブルのデータを全送することに対処する方法についてでした。

Money Forward Developers

Discussion