giftee Tech Blog

ギフティの開発を支えるメンバーの技術やデザイン、プロダクトマネジメントの情報を発信しています。

giftee リニューアルの裏で…【データ移行編】

はじめに

こんにちは。ギフティでエンジニアをしている飯野(@okomeshino)です。普段は主に giftee というC2Cサービスの開発・運用を行っています。

giftee は、「気軽に送れるオンラインギフト」をコンセプトにしたオンライン上で完結するギフティングサービスです。このサービスは長い歴史を持っており、リニューアルを繰り返して成長してきました。前回のリニューアルからは約7年が経っており、様々な課題を抱えるように…。そんな課題を解決すべく、2020年12月1日、再びリニューアルしました!🎉

今回は、そんなリニューアルプロジェクトの裏側を【データ移行】にフォーカスを絞ってご紹介します。大規模なデータの移行はなかなか携われるものではなく、通常の開発とは携わる人数も実施される回数も圧倒的に少ないため、ノウハウやナレッジを探しにくいです。サービス毎に移行の要件やシステムのアーキテクチャも様々なので応用しにくい面もありますが、少しでも参考になれば幸いです。

※ リニューアルプロジェクトの【デザイナーとのコミュニケーション編】はこちら

想定読者

  • giftee のサービスがどのように開発されているか興味がある方
  • giftee のリニューアルプロジェクトに興味がある方
  • サービス移行(特にデータ移行)のプロジェクトに携わっている方

リニューアルプロジェクト概要

データ移行のお話をする前に、知っておいて欲しい前提があるので先にお話しします。

まず、既存の giftee のアプリケーションは主に Rails で作られているのですが、このアプリケーションに「giftee のアプリケーション」と「giftee for Business (以下 forB)(※1) のAPIを提供しているアプリケーション」の2つが混在している状態でした。(いろいろな歴史的背景がありますが割愛します)これにより、「giftee」への変更を行う際に「forB」に影響が及ぶ可能性があり、非常に修正コストが高い状態になっていました。

このように、既存のサービスの課題として一番大きいものが「giftee と forB の混在」です。今回のリニューアルはこの状態から脱却するべく、『giftee のアプリケーションのみを完全に切り離す』ことが1番の目的(※2)です。(他にも細かい目的はたくさんありますが)

※1 giftee for Business ... 法人向けのオンラインギフト販売サービス。キャンペーンの特典に利用していただいたり、ノベルティに利用していただいたり…。様々な用途でお使いいただけるオンラインギフトを法人向けに提供しているサービスです。 ※2 当ブログの過去記事で弊社CTOが詳しく触れています。8 years of giftee.co (2/2)

データ移行のポイント(気をつけるべきこと)

前置きが長くなってしまいましたが、ここからは「データ移行」のご紹介です。

今回は私が移行プロジェクトを経て感じた4つのポイントに絞ってお伝えします。各ポイント毎の概要をご説明した後に「実際に giftee でやったこと何か?」もご一緒に紹介し、データ移行の作業に対する解像度を高めてもらえたらと思います。

1. 移行当日の作業時間を意識する

データ移行はほとんどの場合、動いているサービスにおいて行われることが多いため、システムの停止を伴う作業になると思います。サービスの内容にもよりますが、システムを停止している間は機会損失が生じて売上を落とす要因になります。移行のスクリプトやDBからデータを抽出するSQLを用意する際にはこのことを意識してできるだけ短い時間で移行ができるよう心がけましょう。

また、どうしても時間がかかるようなデータの場合は事前に移行しておき、当日は差分を取り込むよう手順にできないか、ということも考えてみると良いかもしれません。

🎁 giftee でやったこと 🎁

giftee では移行対象の会員データが約160万件ほどありました。このデータ量だと、移行先で1つ1つ DB に INSERT していくと膨大な時間がかかってしまいます。そのため、以下のことを意識してデータの取り込みスクリプトを作成しました。

  • 必ず Bulk insert を行って INSERT の時間を短くする
  • csv を扱う際に1度に処理する行数を制限し、メモリを節約する

giftee では Ruby on Rails を使用しているので、 activerecord-import を利用した Bulk Insert を行っています。また、移行データは CSV で抽出して移行先で取り込むような形にしていたため、CSV を取り扱う際は CSV#each_slice を利用してメモリを節約するようにしました。

BATCH_SIZE = 10000

# each_slice を利用することにより、指定した行数毎に処理を実行することができる
users_csv.each_slice(BATCH_SIZE) do |csv|
  # csv を元に user データを成形して配列にしている処理。ここでメモリを食う
  users = csv.map { |row| attributes(row) }
  # Bulk insert を行ってDBへのアクセスを減らす
  result = User.import(users, batch_size: BATCH_SIZE)
end

2. 移行前のデータの意味を理解する

既存サービスを長年運用してきてデータベースの構造から実データの意味まで理解している人が行う場合は何も問題無いですが、そのような熟練の勇者がいない場合もあります。その場合は既存システムのデータが持つ意味をズレ無く理解することが重要です。

例えば、既存のDBの商品情報を持つテープルに price という名前のカラムがあったとします。移行後のDBにも price というカラムがあるのでそのまま移行を行えば良い、というわけではありません。price という名前が一致していたとしても「税込の価格」なのか等、確認するべき観点がいくつもあります。移行前は「税込価格」としてのデータであったのに、移行後のサービスで「税抜き価格」として扱ってしまっていた、ということになれば取り返しが付きません。もし、データ移行を実施するメンバーに熟練の勇者がいない場合はソースコードまで読み込んでデータが持つ意味を理解した上で移行を行いましょう。

🎁 giftee でやったこと 🎁

データ移行は私が担っていたのですが、私自身は既存サービスの運用経験が全く無いという状態でした。幸い、既存サービスの運用経験が長いエンジニアがいたので、その方と一緒にデータの意味を洗い出す作業を行いました。(197テーブルを1つ1つ見ていくしんどい作業…)

また、洗い出しを行った上で移行先のDBと移行元のDBをカラム単位でマッピングを行い、必要なデータを移行するべきカラムがあるか、データの構造として移行が不可能な状態ではないか等の確認もしています。その中でテーブル定義の不備に気付くこともあり、必要なテーブルを追加するようなシーンもありました。


3. 移行日の作業をスムーズに行えるようにしておく

ポイント1「移行当日の作業時間を意識する」でもお話しした通り、移行作業にかける時間はできるだけ短いほうが良いです。そのために、当日に行う作業についてもスムーズに行えるように準備しておきましょう。

例えば当日行う作業の「手順書」をあらかじめ用意しておき、その内容に沿うことで移行作業をスムーズに行うことができます。また、手順を詳細に記載することによって移行作業の属人化を防ぎ、誰が実施しても同じ結果になる効果もあります。移行の作業は複数のサービスを跨いで行うことも多いので、各種リンクなどを手順書内に収めるとスイッチングコストも抑えられます。

🎁 giftee でやったこと 🎁

giftee でも実際に「移行手順書」「当日のタイムテーブル」を作成して万全の体制で移行作業に臨みました。 こちらは作成した手順書、タイムテーブルのイメージです。

移行手順書(リハーサル用) ※ イメージ

当日のタイムテーブル ※ イメージ

移行手順書はざっくりとした指示書ではなく、手順書だけを確認すれば良いように、実行するクエリが記載されたドキュメントへのリンクを手順書内に記載したり、スクリプトのエラーが吐き出される AWS S3 のリンクまで詳細に書かれています。これにより、この手順書通りに誰が実行しても同じように移行作業が行えます。

また、移行元の作業・移行先での作業といった形で並行作業が行えるものもあったため、当日のタイムテーブルを作成し、並行できる作業を可視化しました。これにより、隙間無く人員を配置できるため、移行の作業を無駄なく実施することができます。


4. リハーサルを必ず行う

「3. 移行日の作業をスムーズに行えるようにしておく」でも触れましたが、リハーサルを行うことはとても大事です(ぶっつけ本番でやろうという人は少ないと思いますが)。しかし、「ただやるだけ」ではダメで、より【厳密に】です。

ここで言う「厳密」というのは「本番相当のデータ量」「本番と同じ手順」で行うこと。ここで大事なのは「本番相当のデータ量」の部分です。なぜ、本番相当のデータ量でリハーサルを行った方がよいのかというと、データ移行にかかる時間がより正確に測れるからです。

リハーサルの第1の目的は「本番と同じ手順で行い、手順やスクリプトの問題を洗い出すこと」ですが、第2の目的として「より正確にデータ移行にかかる時間を計測する」ことだと私は思います。本番相当のデータ量で移行作業を行うことにより、想定以上に時間がかかるスクリプトを発見できるかもしれないですし、逆に想定よりも時間がかからないスクリプトがあるかもしれません。本番で失敗が許されない移行作業において想像だけでは心許ないです。実際に計測して予測値を立てるようにしましょう。

🎁 giftee でやったこと 🎁

giftee でも実際にリハーサルを行いました。私がなぜ前項で「より正確にデータ移行にかかる時間を計測する」を重要なポイントとして置いたかというと、リハーサルを行ったことによって時間の予想に大きなズレがあったことが発見できたからです。

今回、一部機能で外部のサービスを利用している関係で、そのサービスにデータの移行を行う必要がありました。テスト環境でテスト用のデータを用意して予測を立てていたのですが、実際に本番と同じスペックの環境、本番相当のデータ量でリハーサルを行ってみたところ、想定の8倍の時間がかかることが判明しました。

その時間システムを止めてしまうと多大な損失になってしまうため、当日の作業をできるだけ削る必要があります。そのため、外部サービスへのデータ移行は「事前に行っておき、当日は差分のみを取り込む」という方向に舵を切り直しました。この判断ができたのは実際に本番相当と同じ環境でリハーサルを行ったからこそです。この作業を怠っていた場合、この事実に当日気付くことになるかと思うと今でも恐怖です…。

おわりに

今回は giftee のリニューアルに伴うデータの移行作業についてご紹介しました。

データ移行はそれぞれのサービスで目的や背景が違います。そのためオープンな場所で共有し難いトピックではありますが、今回挙げたポイントは多くの移行プロジェクトに共通する内容に触れたつもりなので、これからデータ移行の作業を行う方の一助になれれば幸いです。(本当はもっと詳細に移行で苦労したことや作業の詳細を書きたかったのですが、内容が膨れ上がりそうだったのでまた別の機会にご紹介できれば…)

リニューアルプロジェクトは終わりを迎えましたが、これからが本番です。まだまだサービスの成長のためにやりたいことがたくさんあるので、引き続きユーザー体験の向上と運用に力を注いでいきます。みなさんも是非 giftee を使ってみてください。