Slide 1

Slide 1 text

10年続くサービスのデータを 1日未満のメンテナンスウィンドウで 安全に移管する 2024-10-15 Hatena Engineer Seminar #31 「少年ジャンプ+」 サーバーサイド編 id:masawada / id:momochi29 1

Slide 2

Slide 2 text

2

Slide 3

Slide 3 text

長時間メンテナンスで実施したこと ● 他社システムからGigaViewerへのデータ移行 ● 動作確認 ● GigaViewer for Apps版 少年ジャンプ+の配信 3

Slide 4

Slide 4 text

このセッションのテーマ ● 他社システムからGigaViewerへのデータ移行 ● 動作確認 ● GigaViewer for Apps版 少年ジャンプ+の配信 4

Slide 5

Slide 5 text

このセッションのテーマ ● 素朴に実施すると数週間かかる移行を、どう 1日未満のメンテナンスウィンドウに収めたか ● 巨大なシステムのデータ移行を実施するに あたってのテクニックや心構え 5

Slide 6

Slide 6 text

自己紹介 id:masawada ● データ移行のリード ● 全般的な設計から 渉外までなんでも担当 ● マンガメディア開発の チーム歴は5年程度 ● 「ふつうの軽音部」 が好きすぎる 6

Slide 7

Slide 7 text

二部構成 ● 少年ジャンプ+のデータ移行事情 ○ id:masawada ● 移行の自動検証について ○ id:momochi29 7

Slide 8

Slide 8 text

少年ジャンプ +の データ移行事情 8

Slide 9

Slide 9 text

データ移行は総合格闘技 9

Slide 10

Slide 10 text

データ移行は総合格闘技 ● 旧システムのデータを新システムに横流しする ○ ではない ● モデリングの違いを理解して翻訳する作業 10

Slide 11

Slide 11 text

データ移行は総合格闘技 ● 旧システムのデータを新システムに横流しする ○ ではない ● モデリングの違いを理解して翻訳する作業 ○ だけでもない 11

Slide 12

Slide 12 text

データ移行は総合格闘技 要求の整理 設計 費用見積もり キャパシティプランニン グ 実装 検証 パフォーマンスチューニング スケジュール の検討 リハーサル走行 本番走行 移行後対応 などなど… 30分では話せない! 12

Slide 13

Slide 13 text

今日話すこと ● 少年ジャンプ+のデータ移行に対する要求 ● どのような実装で実現したか ● うまくやるには 13

Slide 14

Slide 14 text

少年ジャンプ +の データ移行に対する要求 14

Slide 15

Slide 15 text

他社システムとGigaViewerの関係 ● 他社システム(旧システム) ○ 旧少年ジャンプ+ アプリ版のバックエンド ○ 2014年9月22日にサービスイン ● GigaViewer(新システム) ○ もともと少年ジャンプ+ Web版を配信 ○ 2017年1月18日にサービスイン 15

Slide 16

Slide 16 text

他社システムとGigaViewerの関係 ● もともと以下データを同期していた ○ 雑誌・話の原稿やメタデータ ○ ユーザアカウント情報 ○ ユーザの定期購読情報 ○ ユーザが購入した雑誌の情報 16

Slide 17

Slide 17 text

他社システムとGigaViewerの関係 ● それぞれのシステムで管理していたデータの例 ○ 話のレンタル履歴 ○ 話をレンタルするためのコイン ○ 閲覧履歴 ○ コメント ○ などなど 17

Slide 18

Slide 18 text

移行するデータの特徴 ● とにかくデータが多い ○ 当初想定では、ほぼ最強のAuroraインスタンスの リソースを使い切って数週間かかる見込み ● メンテナンス直前まで書き込みが発生する ○ 支配的なデータはユーザの閲覧履歴・コインなど ○ マンガを読む機能も課金も止められない 18

Slide 19

Slide 19 text

移行プロジェクトに対する要求 ● メンテナンスウィンドウは1日未満 ○ 日毎に連載があるため ● 移行元の機能ロックは実質不可能 ○ 支配的なデータは止められないため 19

Slide 20

Slide 20 text

どうするか ● ユーザのデータだけをまず移行することに ○ 原稿やメタデータは旧システムに入稿して同期 ○ 別途移行プロジェクトを立ち上げる ● とはいえこれで減らせる量は少ない…… 20

Slide 21

Slide 21 text

どのような実装で 実現したか 21

Slide 22

Slide 22 text

必要なデータを オンラインで少しずつ移行 22

Slide 23

Slide 23 text

分割移行 ● 旧システムのDBスナップショットをいただいて復元 ● 特定の時刻範囲に作成・更新されたデータを移行 ○ を繰り返す 23

Slide 24

Slide 24 text

分割移行 スナップショット 2024/2/1 スナップショット 2024/3/1 2014/1/1 2024/1/1 2014/1/1 2024/1/1 1回目の移行 移行済 2024/2/1 2024/2/1 2回目の移行 2024/3/1 24

Slide 25

Slide 25 text

分割移行 ● Step Functions + AWS Batchを採用 ○ 「魔法のiらんど」のデータ移行でも実績がある ○ 詳しくは: Hatena Engineer Seminar #14* ● Step Functionsで依存関係を管理しつつ実行 * https://speakerdeck.com/tkzwtks/hatena-engineer-seminer-number-14-data-migration 25

Slide 26

Slide 26 text

分割移行 26 { “since”: “2014-01-01T00:00:00+09:00”, “until”: “2024-01-01T00:00:00+09:00” } 引数(時刻範囲) 移行システム 旧システムDB 新システムDB 変換して挿入・更新 時刻範囲のデータを取得

Slide 27

Slide 27 text

分割移行 ● 少しずつ移行する期間を詰めていって、最後は メンテナンス当日に移行 ○ 例: 2014年から5年分, 次の3年分, 1年分... ○ サービス成長に伴って単位時間あたりのデータ量 が増えていく 27

Slide 28

Slide 28 text

分割移行 ● 期間を詰めるほど移行時間が短くなる、ではない ○ Fargateの立ち上げオーバーヘッドだったり ○ 中間状態を管理するためのS3の読み書きだったり ○ どこかで頭打ちになる ● メンテナンス前の最後の分割移行はちょうどよい 塩梅を狙ってタイミングを設定 28

Slide 29

Slide 29 text

うまくいくのか? ● データの依存関係を丁寧になぞれば可能 ○ 2014-01-01〜2015-01-01のユーザアカウント ○ 2014-01-01〜2015-01-01のコイン ○ 2014-01-01〜2015-01-01の読み物購入情報 ■ 購入情報にはどのコインで買ったか記録される ○ 関連がないデータは並行で移行可能 29

Slide 30

Slide 30 text

うまくいくのか? ● 実装をミスると容易に整合性が破壊される ○ 練習用の環境で走行したら実際に壊れた ■ データの更新に失敗したり ■ 更新すべきところで新規に挿入してしまったり ○ 自動検証を仕込むことに ■ この話は後ほど 30

Slide 31

Slide 31 text

分割移行を実現するために必要な条件 ● 移行元に作成日と更新日が仕込まれていること ● 移行元にレコードを物理削除する処理がないこと 31

Slide 32

Slide 32 text

分割移行を実現するために必要な条件 ● 移行元に作成日と更新日が仕込まれていること ● 更新日だけではダメなケース ■ ユーザの最終更新は2024-02-01 ■ コインの最終更新は2023-01-01 ■ 移行範囲は2023-12-31いっぱい ■ →更新日だけでは依存ユーザが作られない! 32

Slide 33

Slide 33 text

分割移行を実現するために必要な条件 ● 移行元にレコードを物理削除する処理がないこと ○ とはいえ、ありますよね ● あった場合は? 33

Slide 34

Slide 34 text

移行元に物理削除があったら ● 移行元で物理削除を記録してもらう ○ triggerなりアプリケーションで仕込むなり ● 移行先に移行ログをとっておく ○ どのテーブルのどのidがどこに移行されたか ● 移行処理の最初に消す ○ 削除ログがある=元のデータは既にない 34

Slide 35

Slide 35 text

移行ログは便利 ● あとから何をどう移行したか調査しやすい ● 依存するデータがあるときはここから引ける ● 処理をべき等にするためにも利用できる ● INSERTするレコード数が単純計算で2倍にはなるが 35

Slide 36

Slide 36 text

ところで…… ● 素朴にオンラインのまま分割移行すると 移行先のDBにユーザのデータが増える ● ユーザにデータが見えるのでは……? 36

Slide 37

Slide 37 text

オンラインのまま移行す るテクニックふたつ 37

Slide 38

Slide 38 text

分ける・隠す 38

Slide 39

Slide 39 text

分ける ● もともとなかった機能に対しての移行ならユーザに 見えない ● 一見同じ機能でも新機能扱いにしてしまう手 39

Slide 40

Slide 40 text

分ける ● ユーザの利便性を損わないとかビジネス上問題が ないとかで判断されるべきではある ● 例: Web用コインとアプリ用コインを分ける ○ (移行都合ではなく、新システムにおける扱いを 検討する際にたまたま分けていた) ○ 今回は移行都合で意図的に分けた例はない 40

Slide 41

Slide 41 text

隠す ● 素直にやるならこっち ○ フラグを立ててリリースまで隠す ● 例: レンタル状態はWebとアプリで統合したい ○ レンタル状態のテーブルにフラグのカラムを追加 ○ リリースまではWebでアプリのものを表示しない 41

Slide 42

Slide 42 text

…という方針を 立てるためには? 42

Slide 43

Slide 43 text

うまくやるには 43

Slide 44

Slide 44 text

うまくやるには ● 最初から理想に至るのは無理 ● どうする? ○ 仕様を読み込む ○ 本番データの傾向を観察する ○ イテレーションを回す 44

Slide 45

Slide 45 text

仕様を読み込む ● 移行の処理自体はモデリングを翻訳する作業 ● 元の処理がどうなっているか分からないと困る ○ 今回は仕様書とデータ一式をいただいた ● 仕様だけじゃなく実際にアプリを触るのも重要 ○ ユーザが触れる面の全てを把握しておく ○ ユーザ=エンドユーザに限らず運用者なども含む 45

Slide 46

Slide 46 text

本番データの傾向を観察する ● どうあがいても例外的なデータは発生する ○ 運用上発生したり ○ 不具合への対応だったり ● 例外的なデータは仕様や開発データを見ていても 気付けない 46

Slide 47

Slide 47 text

本番データの傾向を観察する ● 気付けない例: ○ 仕様にはないけどNullableでNULLが入っている ○ idが指し示す先のデータがない ● どう移行されるのが正しいのか? を考えるためには データを見つつ仕様を読み込む必要がある ○ 考古学 47

Slide 48

Slide 48 text

イテレーションを回す ● 未完成でもとにかくイテレーションを回す ● 1回実行してうまくいくことは絶対にない ○ なんなら時間の見積もりですら実装やデータの 状況によって変わっていく ● エラーを1回で出し切れることも絶対にない ○ エラーを潰すと依存していた処理でエラーが発生 48

Slide 49

Slide 49 text

イテレーションを回す ● イテレーションを回しやすくする ○ 全量の移行を実施すると時間がかかりすぎる ○ 一部ユーザだけ移行して動作確認しやすいような 仕組みを導入 49

Slide 50

Slide 50 text

イテレーションを回す ● イテレーションを回す障害を取り除く ○ Step Functionsは一箇所で異常終了すると ステートマシン全体が止まる ○ 例外的なデータでいちいち止めていたら処理が 一生終わらない 50

Slide 51

Slide 51 text

イテレーションを回す ● イテレーションを回す障害を取り除く ○ 1イテレーションの中ではエラーがあっても途中 で止めずに最後にエラーが起きたかどうか判断 ○ 個別の処理ではエラーを記録するだけにしつつ エラーがなかったように振る舞う 51

Slide 52

Slide 52 text

ということを意識して 移行の仕組みを作ると よいのでは 52

Slide 53

Slide 53 text

ここまでのまとめ ● 分割移行でメンテナンスウィンドウを短くした ○ 19時間のメンテナンスのうちデータ移行は6時間 ● オンラインのまま移行するためのテクニック ○ データを分ける・隠す ● うまくやるためには ○ 結局は泥臭く・丁寧にやる、以上のことはない 53