運用中のマンガサービスに「GigaViewer for Web」を導入する場合のデータ移行事例

こんにちは、 GigaViewer for Web の開発・運用を担当している id:yunagi_nid:mds_boy です。 今回は、運用中のマンガサービスに GigaViewer for Web を導入する際のデータ移行の事例を紹介します。 この記事では、過去に Inside GigaViewer for Apps 第6回目 の記事で行った事例と比較しつつ、やったこと、やらなかったことを紹介していきます。

データ移行プロジェクトの概要

今回のデータ移行では、 GigaViewer for Web を導入していない、すでに実運用されているサービスから、 GigaViewer for Web を利用したサービスへと移行する事例について扱います。

どんなデータを移行したか

今回、移行してきたデータは次のようなものになります:

  • ユーザー毎のポイント残高とメールアドレス
  • マンガの書誌データ (タイトル、著者情報、サムネイル、マンガ原稿データなど)
  • いいね数

今回はおおよそ4~5ヶ月後のリリースが決まっており、短期間にサービスの開発とデータ移行をする必要があったため、出版社さんとの話し合いの結果、前述したデータのみを移行対象とすることにしました。

移行要件

データ移行の要件として、各データについてそれぞれ次のような要件がありました。

  • ユーザー毎のポイント残高とメールアドレス
    • 既存ユーザーが不利益を被ることがないようにするため、データの不整合が起こらないようにする
    • また、ポイントには有効期限があり、メンテナンスイン時点で有効期限が切れているポイントは移行してはならない
  • マンガデータ
    • 短期間で入稿できるように、簡単に入稿できるようにするためのデータ整形が必要
    • ただデータをコピーするだけではなく、今後の運用も見据えて考慮した結果、最終的にはマンガ本体のアーカイブデータと、それに対応する CSV ファイルを作成する方針に
  • いいね数
    • プロジェクト開始当初は対象外としていたが、リリース後に出版社さんからの要望があり実施
    • リリース後から、すでにいいねされた数とリリース前にすでにいいねされた数をうまく合算する必要があり

これらの要件をこなしながら、短期間でどのように移行するかが勝負でした。

どのようにデータ移行を行ったか

DB スナップショット、およびマンガデータの共有は、S3 のクロスアカウントアクセスを設定することで、アカウント間を跨いでデータをコピーできるようにしました。 コピーそのものは、 GitHub Actions の workflow_dispatch トリガーを利用して、ボタンをクリックしてワークフローをキックするだけで同期できるようにしました。 具体的なデータ共有の流れとしては、先方所有のバケットに共有データがアップロードされ、それをはてな側のバケットに GitHub Actions を使ってコピーをし、コピー後、先方所有のバケットからはデータを削除してもらうというものでした。

ユーザーデータ移行

今回のユーザーデータ移行では、全データ一括での移行を行いました。これは、移行データ量が予定したメンテナンス期間内に収まる範囲だったこと、分割移行することで考慮事項が増えるのを避けたかったためです。この方針により、移行作業をシンプルに進めることができました。

移行スクリプト実装に取り組む前に、社内の過去事例を参考にして、移行元と移行先のデータ項目を紐付ける対応表の作成を行いました。移行元のデータが移行先でどう保持されるかだけでなく、移行先のデータが移行元のどこから得られるか、という視点でも確認することで、考慮漏れを早い段階で減らすことができました。

実際のユーザーデータ移行では、各ユーザーの移行情報を保持する一時的なテーブルを用意し、次のような流れで移行を行いました。

  1. (データ集計)集計ポイント残高を集計し、メールアドレスと共に、一時テーブルに挿入
  2. (検証)一時テーブルに対して、検証用スクリプトを実行したり、実装に関わっていない第三者の方によるチェックを行う
  3. (本番反映)一時テーブルからデータを取り出し、本番運用しているテーブルに反映

移行したデータの内、ポイント残高は集計して算出する必要があり、金銭にも関わる情報なので、しっかり複数人でチェックする段階を設けました。 もし移行情報に何か不整合があっても本番環境のデータには影響が出ないよう、検証が済んだ上で本番運用しているテーブルに反映させる流れとしました。

移行スクリプトが準備できた段階で、移行元の担当者の方から受け取ったその時点の移行データを用いて、移行検証用の環境で移行テストを行いました。これにより、移行に掛かる時間をある程度見積もったり、実装に不備がないかの事前確認ができました。

マンガデータの移行

今回、マンガデータの移行には、先方からいただいたデータ構造をもとに、 GigaViewer for Web で扱える形式の書誌データへとデータ構造を書き換えるためのワンタイムスクリプトを TypeScript で用意しました。 これは、データ移行元のサービスによって、提供されるデータのデータ構造が大きく異なることや、短期間での移行だったため、普段ワンタイムスクリプトを用意している Perl よりも型による安全性や、開発スピードを優先したことによるものです。 また、 DB 接続は行わずに、 Redash であらかじめ関連するデータを SQL で記述し、 CSV としてエクスポートしたものをデータソースとして利用しました。 これは中間データとなる CSV データも移行時の検証や、先方でのデータ入力に便利だったことから、この形式を取ることになりました。 作成したワンタイムスクリプトは tsx を用いて、

$ tsx /path/to/onetime/01-script.ts

という形で実行できるようにし、いくつかのフェーズに分けて移行しました。 実際に行ったフェーズは次の通りです:

  1. 原稿データのマイグレーション
  2. CSV を出力するための SQL の作成
  3. 作品単位でのマンガデータの作成
  4. エピソード単位でのマンガデータの作成
  5. 提出データと GigaViewer でのデータの紐付け作業

フェーズ1では、同期したマンガデータを GigaViewer で扱える形式へと変換し、運用会社さんがアクセス可能な S3 バケットへアップロードします。また、この段階で作品毎、エピソード毎に GigaViewer で扱いやすくするための ID を発行し、マッピングします。
フェーズ2では、 Redash 上で CSV を作成するための SQL クエリを出力します。このクエリを Redash へコピー&ペーストすることで、中間データが得られます。
フェーズ3では、 Redash 上で出力した CSV をもとに、作品単位でのマンガデータを作成します。タイトル、著者情報、作品説明、などがここに含まれます。 GigaViewer では入稿時に CSV を使用しているので、ここで入稿用の CSV を作成します。
フェーズ4ではエピソード単位でのマンガデータを作成します。エピソードタイトル、エピソードの通し番号、配信開始日時、価格情報などがここに含まれます。作品単位のマンガデータと同様に、こちらも CSV を作成します。
フェーズ5では、紐付け作業をします。ここで作成するデータには、のちに移行元サイトから GigaViewer へのリダイレクト処理を行う際に必要なデータや、マンガデータを一覧して確認するためのデータなどが含まれます。

いいねデータの移行

いいねの引き継ぎには、先に述べたスナップショットから対応するテーブル、カラムのデータを取り出し、マンガデータの移行フェーズ2のように SQL を生成する方法を採用しました。 ただし、いいねはリリース後に先方から要望があり移行したというのもあり、いいね数は移行後に増えたものもあるということ、移行後、まだいいねされていないものはレコードが存在しないというものがあったため、次のようなクエリで対応しました:

INSERT INTO likes (series_id, like_count) VALUES ($series_id, $like_count) ON DUPLICATE KEY UPDATE like_count = like_count + $like_count;

具体的には、次のようなデータを、作品の数だけ流し込みました。

-- 作品ID=1に対して、いいね=5を加算
INSERT INTO likes (series_id, like_count) VALUES (1, 5) ON DUPLICATE KEY UPDATE like_count = like_count + 5;
-- 作品ID=2に対して、いいね=10を加算
INSERT INTO likes (series_id, like_count) VALUES (2, 10) ON DUPLICATE KEY UPDATE like_count = like_count + 10;
-- ...以下、作品数だけ続く

INSERT ON DUPLICATE KEY UPDATE (MySQL ドキュメント) を使っているのがポイントです。これにより、データが存在する場合は更新を、存在しない場合は作成を実現できます。

移行の予行演習

移行当日の少し前には、当日の移行スケジュールに合わせ、ユーザーデータおよびマンガデータ移行の予行演習を実施しました。 実際に順番通り移行作業を行ったことで潜在的なタスクが発見され、移行作業をより明確にすることができました。

今回のデータ移行のポイント

第一に、移行元の担当者の方々が協力的であったことが大きかったです。丁寧な資料を送って下さったり、質問やデータの受け渡しに快く応じてくださり、たいへん助かりました。
また、移行当日や移行後に大きな問題が発生しなかったのは、検証用の環境を使って丁寧に検証ができたこと、第三者にも検証してもらったこと、予行演習をしたことなどが要因としてあると思います。最初期の実装では考慮漏れなどもあったのですが、繰り返し検証することで比較的早期に発見でき、移行直前に慌てることもありませんでした。
マンガデータは容量が大きいのと、運用会社さん側で入稿作業が別途発生するので、データの納品が行われるたびに、差分データを作成していきました。これは、入稿データが多いと、メンテナンスイン〜メンテナンスアウトまでの時間で、入稿が完了しない恐れがあるためです。
逆に、いいねでは一括インポートを行いました。これは単純な SQL で実装が可能だったこと、また本番同等の試験環境で実施した際、実行時間が十分に短かったからです。

終わりに

今回は運用中のマンガサービスに GigaViewer for Web を導入する際のデータ移行の手順について紹介しました。 短期間でのデータ移行でしたが、規模と要件に合わせた方針を立て、丁寧な検証と予行演習により、大きな問題なくリリースできました。 今後、同様のプロジェクトに取り組まれる方の参考になれば幸いです。