マイクロサービス

【マイクロサービス】異なるDB間でデータ移行をした話

2022/07/17

背景

仕事先ではマイクロサービスを採用しており, サービスごとにDBも分かれている.
今回, A機能でお客様が設定したデータとB機能でお客様が設定したデータを統合して、A機能B機能どちらでも同じ設定を利用したいという要望に対応することになった.

最終的にとった手法

紆余曲折あったが最終的には,

  1. 設定を追加・編集するAPIをFeature Flagで塞ぐ
  2. DB間のデータ移行を行う
  3. 設定を統合した後に動くようなコードをFeature Flagの切り替えでリリースする
  4. 設定の追加・編集するAPIを元の戻す(公開する)

という形にした.

主要なデータ移行コマンド

実際に作成した移行スクリプトはもう少し環境の判定や移行後のDBデータの比較などをやっているが, 一番悩んだデータ移行部分のみを紹介する.

やっていることはシンプルで, 元のDBから対象データをCSVとしてローカルに出力し, そのCSVを移行先のDBにコピーする.
標準出力を利用してコピーをすることで, さらにシンプルにすることもできるが, 移行したデータを元に元に戻すスクリプトなども作成していたため, ローカルに出力するようにした.

PGPASSWORD=[コピー元DBのパスワード] psql --username=[コピー元DBのユーザ名] --host=[コピー元DBのホスト名] --port=[コピー元DBのport番号] [コピー元DBの名前] -c "SELECT column_a, false AS column_b, column_d AS column_c FROM [スキーマ名].[テーブル名];" -A -F $'\t' \
  | sed -e '1d' \
  | sed -e '$d' \
  > ./output.csv

PGPASSWORD=[コピー先DBのパスワード] psql --username=[コピー先DBのユーザ名] --host=[コピー先DBのホスト名] --port=[コピー先DBのport番号] [コピー先DBの名前] \
  -c "\copy [スキーマ名].[テーブル名](column_a, column_b, column_c) FROM ./output.csv WITH CSV NULL AS '''' DELIMITER E'\t';"

解説

psqlコマンドで出力される表形式だと無駄な文字列などが入っているため, -A オプションを設定することで位置揃えなしの出力モードに切り替える.

-F $'\t' オプションを追加することで, 区切り文字を水平タブに設定する.
※お客様が設定項目にカンマ(,)を利用している可能性があったため

sed -e '1d' でヘッダー行(カラム名)を削除し, sed -e '$d' で行末(n rows)を削除する.

水平タブが区切り文字のCSVで出力したので, WITH CSV NULL AS '''' DELIMITER E'\t' を指定し, 出力したCSVのデータでnullの部分は空文字にしたかったため, NULL AS ''''を指定した.
※空文字を指定したい場合は ' が4つ必要なことに少しハマった

おまけ

データにカンマが入っている可能性があったため, 区切り文字を水平タブにしたが, 水平タブもデータに入っているか念のために確認した.

[カラム名] like '%'||CHR(9)||'%'

学び

最初はお客様に一切影響が出ないように, データ移行用のコードをリリースし, その後データ移行をフラグデータを込みで実行し, 統合後のコードをリリースしてお片付け.
などの計画でプロジェクトが動いていたが, 設計が複雑になることで, 他メンバーとの認識合わせによるコミュニケーションコストが爆増してしまい, 途中で見直すことにした.

見直した際に, 今回は設定画面で頻繁に触る場所でもなく, お客様の利用が比較的少ない時間にリリースするのであれば, 関係する箇所を利用できないようにするのはどうかと聞いたときは, そんなことをしても良いのかと少し驚いた.

結果, お客様から問い合わせが1件もなくデータ移行することができた.
※APIが停止する期間が短くなるように準備はちゃんとした

今回の件を通して, 自分の選択が本当にお客様のためになるのか?ということを考えさせられた.
サービスは止めてはいけないものだと思っていたが, そこを意識しすぎて無駄な工数をかけてしまうと, 今対応している機能の提供も遅くなり, それによって関係しているエンジニアが生み出す未来の機能も提供が遅れてしまう.
そこと比較した際に, 20~30分お客様が触るかもわからない部分を止めないことに価値があるのだろうか?

Twitterフォロー待ってます!