双方向データ連携プロジェクトで利用した「SymmetricDS」に関するナレッジの供養

日常の業務でQiitaTeamを活用しており、業務の情報共有などに利用してます。
で、昔自分が書いたものでなんかネタないかなぁと探し回ったところ、非常に苦労したデータ連携プロジェクトに関する記事を見つけました。
触っていたツール「SymmetricDS」を見たらエモくなったので、供養も含めて世に送り出そうかと思います。

# 背景と記事概要
数年前、運営している「入札情報速報サービスNJSS」で扱っている技術がレガシーとなってきたため、サイトを1から作り直す大規模プロジェクトがあリました。
リニューアルに伴いNJSSが溜め込んできた過去の案件情報、ならびに毎日登録される案件情報をシステム間で連携する必要が出てきたため、データ連携プロジェクトが発足しそのメンバーの一員としてjoinしました。
詳細は後述しますが、技術検討の結果「SymmetricDS」というツールを利用することになりました。

ここではSymmetricDSとはどんなものか、そしてどんなところにつまづいたのかということを開発者目線で掘り下げて書いていこうと思います。
(ツールの良し悪しではなく、使いこなすために障害となったことという観点で記載していることをご理解いただければと)

ちなみにこのプロジェクト、プロジェクト運営の観点から過去に当時のリーダーがDevelopersBoostで登壇してたりもしてます。
https://event.shoeisha.jp/devboost/20221209/session/4112/

# SymmetricDSで何が解決できるの？
https://www.symmetricds.org/
> SymmetricDS is open source database replication software that focuses on features and cross platform compatibility.

公式の言葉を借りるとSymmetricDSはオープンソースのデータベースレプリケーションソフトウェアです。
SymmetricDSは単にDBを複製するだけでなく、複雑なカラムの変換も可能なのが特徴です。

プロジェクトの要件として、
- リアルタイムかつ双方向でデータ連携ができること
- 連携元の複数テーブルに跨ったカラムから連携先の1テーブルに対しルールを適用して変換処理ができること
- コード管理できること

などがあったため選択されました。
データベースにとどまらずファイル連携もできるようで、現在も更新されているきちんと使えるツールです。

# SymmetricDSのアーキテクチャ構造

### 概要図
SymmetricDSはDB間をノードと呼ばれる中間層を返すことによって、データ連携の仕組みを実現しています。

例として以下の図をご覧ください。
3つのDBが用意されており、それぞれにNodeが紐づけられてます。

Node0とNode1,2間で通信を行うような構成ですが、
Node ←→ DB間はJDBC、 Node ←→ Node間はHTTPS通信を行うことでデータを連携することができる訳です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562264/dd83516f-9c41-62dd-4b88-65939fec1802.png)
参考元 : https://www.symmetricds.org/doc/3.14/html/user-guide.html#_overview

### 内部のアーキテクチャ
もう少し内部処理について掘り下げてみましょう。
便宜上、左側のDBを送信元、右側のDBを送信先とします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562264/055a33ea-d35e-74ce-4045-447eac5502b2.png)
参考元 : https://www.symmetricds.org/doc/3.14/html/user-guide.html#_architecture


送信元のノードでは以下の処理を行います。
|処理 |詳細 |
|--- |--- |
|Capture|変更がないか随時監視して変更情報を取り込む |
|Route|送信先のNodeを決定する|
|Extract|変更情報を元に連携するべきデータをDBから抜き出す|
|Transform|データの変換|
|Outgoing|送信|

そして送信先では以下の処理を行います。
|処理 |詳細 |
|--- |--- |
|Incoming|受信|
|Transform|データの変換(送信元だけではできない変換がある場合、こちら側でも可能)|
|Load|DBに登録・更新・削除|

この一連の流れにおいてSymmetricDSはデータ連携を実現してます。
実際の開発においてはソースの80%以上がTransformの処理のため、基本的にはTransform処理が書ければなんとかなります(なんとかならない時もあります)。

### Captureを深掘り
個人的に学びが多かったCaptureについてもう少し掘り下げさせてください。

リアルタイムが売りのSymmetricDSですが、
実際のところはトリガーを利用してデータベース更新操作を保存し非常に短い時間でまとめて処理するバッチ型のツールです。

下記に簡単ですがCaptureの仕組みを記載してみました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562264/eb69e418-8cd9-c4c9-e2e5-6155ba849fe9.png)

SymmetricDSでは初期設定を行うと監視対象のテーブルに対してSYM_ON_XX(テーブル名)というトリガーを作成します。
このトリガーは各テーブルの変更検知に利用され、テーブルが更新(INSERT,UPDATE,DELETE)されたら、どのような変更がされたかsym_dataテーブルに変更履歴を残します。
sym_dataに溜め込まれた変更履歴は一定時間後、SymmetricDSのバッチ処理で読み込まれ、Transformなどの処理が行われていくという流れになります。

このため、一時的にSymmetricDSが止まってしまってもsym_dataにデータが蓄えられ続けられ、途中から再開することも可能となっております。
後継の単方向データ連携プロジェクトではこの構成の仕組みを活かしてスマートな形にデータ連携の仕組みを作成することができました。


# SymmetcricDSの辛かったところ
このようにデータベース連携においてSymmetricDSはよくできたツールなのですが、最終的には使うのをやめました。
開発当初から辛さがあったのですが、そこについても共有したいと思います。

## 情報の少なさ
序盤から開発は難儀しました。その一番の原因がなんと言っても圧倒的情報不足によるものです。
いますぐGoogle検索していただければわかると思いますがこの記事を執筆している当時で約36,000件ほどの記事しか出てきません。
弊社の別プロジェクトで使っているLaravelの約69,000,000件と比較するとその差は歴然です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562264/6175c49e-9712-b31e-bc00-3236f4f31eb1.png) ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562264/8a5795a8-9208-ea70-3afe-3d171d973433.png)


基本的には公式のドキュメントを穴が開くほど読み込み、
それでも分からない場合はオープンソースなのでソースコードを読みに行くということをしてました。
(今考えるとしんどいし当時はもっとしんどかった)

コードを読み込むことで意図的に登録をskipさせる`IgnoreRowException`を発見できたり
ブレイクスルーを起こしせたこともあったのでエンジニアとしては一体験できてよかったなと思います。

## テストが難しい
(ナレッジがなかったからかもしれませんが)データ連携という性質上、テストは難しいです。
特にSymmetricDSは内部の処理はツール側にラッピングされているため、単純な変換処理ならまだしも、変換順序を伴うパターンをテストするのは容易ではありませんでした。

NJSSは同じようなデータ登録でも、データ登録順序に差異があるパターンもあり、
そういった開発・テストをするのは非常に骨が折れました。
ある時はAとBというテーブルにレコードが同時に登録されるが、ある時はA→Bという順序で時間差でレコード登録されるなど。

以下、私たちが実際に行なっていた開発方法です。
1. 画面なりを操作してDBが吐き出すSQLの更新ログを取得
1. 実行されたSQL文をローカル環境で叩けるように関連するマスタ系のデータを引っこ抜いて、ローカル環境にimport
1. 1を実行
1. 連携が想定通りか確認

このため非常に手間と時間がかかり、何度もデグレが起きる状態での開発となってました。

## エラー対応がしんどい
難儀しながらもなんだかんだ無事運用に乗せることができた私たちでしたが、その運用も大変でした。

一度エラーが発生すると永遠にエラーが通知されます。
そして、後続の連携が全てStopします。
SymmetricDSはリアルタイム性が売りなのでこれは仕方のないことですが、
sym_XXXX_error系のテーブルから原因となるレコードを割り出して、
なんで悪いのかを推測して処理を直して、
やっと1個エラーを解消したと思ったら次もまたエラー、次もまたエラー…

と終わりの見えないエラー対応が始まります。
その日の作業放り出してエラー対応です。
運用していてつらみのある仕組みだったなぁと思います。
(朝5時頃になぜか定期的に起きるエラーのせいでみんな早起きになったのはいい思い出です)



# まとめ
SymmetricDSは双方向連携を実現する上でツールの選択肢として良いものを持っていると思います。
が、それを開発・運用できるだけの知識をつけてからでないと痛い目を見るツールでもあります。
それこそ他の代替案でも実現可能なプロジェクトからスモールスタートして知識を高めていき、
徐々に大きいプロジェクトに適用していくのが良きかなと思います。

# 公式サイト
新しいツール開拓者が出ることを祈って。
https://www.symmetricds.org/
https://github.com/JumpMind/symmetric-ds