AWSアカウント間のS3, DynamoDBデータ移行計画の記録(データ完全性検証方法の検討)

DynamoDB テーブルのデータにおいて同一であることを確認する方法を検証しました。
2021.06.15

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こむろ@事業開発部です。

前回 の続きです。

そろそろ諸々の記憶が薄れ始めている頃なので早めに全部書ききっておきたいところです。

前回までのまとめ

データの取得と転送方法についての検討は終わりました。

  • Amazon S3 の転送方法の検討と想定される時間の計測 → 完了
  • Amazon ElastiCache for Redis の転送方法の検討と想定される時間の計測 → 完了
  • Amazon DynamoDB の転送方法の検討と想定される時間の計測 → 完了
  • 転送対象の絞り込み → 完了
    • 重要データ、ログイン情報の3テーブルにフォーカスを絞る
    • 認証情報は除外
  • データ完全性検証の方法検討 → 未着手
  • 全体のスケジュール作成 → 未完成
  • 転送作業の実施 → 未着手

今回のテーマ

データ完全性検証の方法を検討します。

データ完全性検証について。ここでは、「移行元のデータ」と「移行先のデータ」が完全に一致していることを確認することを指します。それ以上でもそれ以下でもありません。

前回記述した通り、重要データα, β, ログイン情報のそれぞれ 1100 万件は、データが完全に同一であることを保証する必要があります。1 バイトのズレも許されないのが要件です。従って、以下を確認できる方法を検討していきます。

  • 移行元、移行先の件数が完全に一致している
  • 移行元、移行先のデータ詳細が完全に一致している

データ完全性検証方法のまとめ

最初にざっくりまとめておきます。今回我々が検証方法として採用したのは以下になります。

  • 件数チェックは 2 回それぞれ別の方法で実施
    • 移行元はDynamoDB JSON ファイルの Line 数をカウント。移行先は Fullscan でカウント
    • 移行元、移行先の DynamoDB JSON をそれぞれデータベースのテーブルに突っ込んで集計
  • データ完全性検証は、DynamoDB JSON データをデータベースのテーブルに投入して比較検証
    • md5 計算は、 Postgresql のテーブルに Insert する際に自動で計算。この値を比較して差分を検出
  • 差分件数が 1 %以内かどうかを判断基準
  • 差分が出た際には、差分の DynamoDB JSON形式のファイルを作成し、再度転送ソリューションへ流し込む

以降は、他にどのような検証を行ったか、なぜそのような方法をとったかの経緯やいくつかの候補の検証結果です。

今回もまた比較的冗長で長めの内容です。興味のある方のみお読みいただければ幸いです。 *1

件数一致の確認方法について

転送処理完了後、まず行うのが転送されたデータ件数の確認です。移行元と移行先においてデータ数で差異がないかを確認します。

ここですでに大幅な差異がある場合は、転送処理を 1 から全てやり直しを含めて検討する必要があります。

件数数え上げの対象は DynamoDB の各テーブル(重要データα, β)です。

前回説明した通り、テーブルはすべて On-Demand Capacity の設定がすでにされているため、負荷に応じて自動的にスケールが行われます。そのため、件数の数え上げ方法については、 DynamoDB の対象テーブルに対して全力で Fullscan をかけて件数をカウントする。 を採用しました。

Amazon DynamoDB - 結果での項目のカウント

以下のようなコマンドで、計測にどれくらい時間がかかるかを確認しました。

$ date;AWS_PROFILE=YOUR_PROFILE aws dynamodb scan --table-name export_from_data_table --select COUNT --query Count; date
2021年  5月 21日 金曜日 16:17:12 UTC
11000000
2021年  5月 21日 金曜日 16:25:21 UTC

1100 万件のデータに対して約 10 分弱で計測が完了できました。

テーブルに Fullscan を実行するため一時的に Read Capacity Unit が跳ねあがります。従って事前に周知しておく必要があります。リソース負荷状況監視のためのアラート等が設定されていることがあるでしょうし。

移行元の件数に関しては、今回は Point in Time Recovery (以降 PITR) で取得したスナップショットデータを利用しており、すでに S3 にダンプファイルが保存されています。このデータの行数を数え上げて移行元のデータ件数としました。 *2

$ wc -l $W_DIR/s3_data/ex/export_from_data_table/*.json
     710563 /home/ec2-user/work/s3_data/ex/export_from_data_table/5gvwlkb46a6jlixsjflkgt3yqm.json
     710427 /home/ec2-user/work/s3_data/ex/export_from_data_table/5mhbt3jbma62rafv5cl7wuu2wm.json
     710597 /home/ec2-user/work/s3_data/ex/export_from_data_table/5oqz3tkmxezhhjm4rt6jdaze4i.json
     613651 /home/ec2-user/work/s3_data/ex/export_from_data_table/c256uv5q2u3ezb2v753mprx5pe.json
....

ふんわりした概要図は以下になります。

移行先の AWS アカウント内に一時的に EC2インスタンスを立て、そちらでコマンドを実行しています。

DynamoDB コンソールの項目数で確認できないのか?

上記数え上げの方法については、当然ながらある程度のコストがかかります。もっと簡単でコストのかからない方法はないのでしょうか?例えば DynamoDB のコンソールには Item Count が表示されています。これは利用できないのでしょうか?

DynamoDB のコンソール等で表示される Item Count は表示に時間差があり、登録した結果が即時反映される値ではありません。そのため、この値をもって検証することはできません。別の方法で確実にカウントする必要があります。

再投入可能な最大の試行回数

再投入のリトライが何回まで試行できそうか、を考えておきます。

前回検証した通り、1100 万件のデータ転送にはおよそ 30 分かかると推定しています。転送処理完了までバッファ込みで最短 2 時間 30 分を作業時間としているため、もし差分が発生し、データの再投入を行う場合には、この時間内に収める必要があります。

今回の計画では再投入や何らかのトラブルによる対応のため、バッファ 1.5 時間を見積もっています。再投入だけで利用することを考えるならば最大で 3 回のやり直しができそうです。

作業進行可否を判断するしきい値について

個人的にこういった作業の際には、リトライをするのか、撤退するのかを判断するチェックポイントが必要だと考えています。そこで上記件数チェックで差分が発生した際の判断基準を設定します。

今回は件数チェックを行っているため、件数 にフォーカスしたしきい値を設定しました。

  • 1 % 以上の差分が出た場合は、作業を一旦停止し、ステークホルダーと調整を行い判断する
    • 件数、残り作業時間を鑑みて判断する
  • 1 % 以下の場合は、全体の転送処理を再実行する
    • 当然関係各所には通達した上で実行します

1100 万件の 1 % なのでおよそ 10 万件となります。この値を判断のためのしきい値としました。

あまりはっきりとした根拠はないのですが、アクティブユーザーが転送対象のおよそ半数 500 万程度と想定し、転送できていない 10 万件のデータがすべてアクティブだった場合 *3、 2 % のユーザーに影響が出ます。

一斉に問い合わせがあることはないと思いますが、サポートセンター等へ 10 万件の問い合わせが行われると、サポート業務がスパイクする可能性が高く、日常のサポート体制では追いつかなくなる可能性があります。最悪ユーザーの離脱等を考えると 2 %という数字は小さい数字ではありません。 *4

上記のような判断から全体の 1 % 以上のズレが生じた場合は、一度対応を中止し、顧客と対応を調整するという方針にしました。

件数一致の検証のまとめ

これで、件数一致の検証について の行動指針が決まりました。実行、確認、エラー時の判断基準はそれぞれ以下のとおりです。

  • 件数の数え上げは DynamoDB の Fullscan とダンプした S3 ファイルの Line 数で計測、比較する
  • 1 % の差分が出た場合は、作業を一旦中止し相談。それ以下の場合は、即時再投入を検討
  • 再投入は最大 3 回まで試行できる
    • テーブルごとに並行して実行可能なので、1 回につき 2 テーブル同時実行も可能

データ完全性の検証

続いて、重要データの転送には大事な要件があります。

移行元、移行先のデータが完全に同一であることを保証しなければなりません。重要データのうち 2 つ(重要データα, β)が完全性検証の対象です。

  • 重要情報は移行前と移行後で 1 バイトの差分も許されない(MUST)

リソースの利用費用が多くなってしまっても短時間で完了できるソリューションを検討する

案1. 移行元の JSON と移行先のデータと比較する

DynamoDB のテーブル情報を転送するソリューションを流用します。移行先のテーブルへのアクションを書き込みから読み込みへ変更し、検証ロジックを追加します。

転送時間とほぼ同等の時間、30分程度で検証が完了できそうです。ただ課題として以下があげられます。

  • 移行先のテーブルからデータを読み込むロジックを実装する必要がある
  • データすべての Attribute を検証するロジックを新たに実装する必要がある ※
  • 再チェックをしたい時に毎度30分かかってしまう

ソリューションはそのまま使い回すわけには行かず、読み込み・検証専用の実装がある程度必要になりそうです。また、再チェックする際にわざわざJSONを解凍して一つ一つ確認するのは少し非効率な気がします。

※ データの確認ロジックについては、後述する別の方法でも利用していますが、データ全体のハッシュ値を計算し、それを比較して差分がないことをチェックするようにします。attribute 一つ一つをチェックする必要がなく同一であることを確認することができるはずです。

こちらがまず 1 案です。すでに登録処理である程度検証済みですので、処理速度等に関しては大きな変化はない想定です。

Write Capacity Unit ではなく Read Capacity Unit をスケールさせるため、多少のパフォーマンスの変化はある前提

差分を抽出したい場合、移行元を全件シークして移行先に存在しないものを記録しておく必要があるので、ここも少し実装が必要になりそうです。

案2. データベースを利用した SQL による検証

移行元、移行先のデータは転送作業時はすでに静止状態にあります。従って時間経過による変更はありません。

移行元、移行先共に稼働アプリケーションのコンテナ数を 0 にして一切データへのアクセスがない状態にしている

従って一度ハッシュ値を計算し、そのデータを保持しておけば何度も計算する必要がなくなります。特に移行元のデータに関しては、PITR からの復元の段階ですでに停止しているため、Export to S3 のバックアップ直後に並行してハッシュ値を計算しておけば、転送完了後は移行先のデータのハッシュ値を全て計算すれば良いだけです。

そこで、RDB に移行元、移行先のハッシュ値テーブルを作成し、SQL によって差分を検出する方法を検討しました。概要は以下になります。

EC2 インスタンスを一つ立ち上げ、その中に Postgresql を立ち上げて利用します。

移行元、移行先の両バックアップデータは、単一の S3 Bucket に格納しています。Python の簡易スクリプトを利用して構築したデータベースに取り込みます。テーブルへ登録時にハッシュ値が計算され、Export データと一緒に登録されます。

文字列として表現されている DynamoDB JSON についてですが、同じデータ構造であれば同じ文字列として出力される(順序が変わったりすることはない)という前提で md5 によるチェックとしています。ここは特に仕様等で書かれていたわけではないので、何度か試行してみて変わらないことを確認したというのが正直なところです。

実際には仕様が変わる可能性があるため、データ構造の検証方法については都度確認が必要かと思います。

差分の結果はマテリアライズド・ビューに保持します。

これは万一差分があった場合、更新対象データを抽出するために、差分データに対し別条件のクエリを素早く実行する必要があります。差分検証で一度きりの実行であれば不要かもしれません。 *5

上記構成を組んだ上で、データが同一であることを確認するには以下の手順で SQL を実行します。

  1. マテリアライズド・ビューを更新 psql -h localhost -U postgres -d ${DB_NAME} -c "REFRESH MATERIALIZED VIEW diff;" 数分で完了
  2. 念の為、再度件数チェック psql -h localhost -U postgres -d ${DB_NAME} \ -c "select * from (select count(id) as before_total from before) as b, (select count(id) as after_total from after) as a;" 2 分程度で結果を出力する
  3. 差分を検出 psql -h localhost -U postgres -d ${DB_NAME} -c "select count(*) from diff" 1分以内に完了

上記は 1 テーブルの検証時間になります。データベースへの投入に最大で 10 分(※)ほどかかるため合計で 15〜20 分少々で検証が完了できそうです。案 1 と比べるとかなり高速です。

上記検証から結果として データ完全性検証はデータベースを利用した方法を採用しました。

検証速度の速さ、SQL による差分対象抽出の容易さなどから、今回のユースケースにおいてはマッチしていると判断しました。

※ Export された DynamoDB テーブルのデータダンプのファイル数によって左右されます。すべてのテーブルは件数としては同じ 1100 万件なのですが、ファイル数が異なります。今回はファイル単位で並列化するようデータ取り込み用のスクリプトを実装をしています。分割されたファイル数が多い方が並列に Insert 処理が実行されるため、短時間で完了します。しかし、ファイル数が少ない場合、つまり 1 つのファイルに記述されているアイテム数(行数)が多い場合、最大で 10 分ほどかかることを確認しています。

差分が発生した場合の対応

データ完全性検証において差分が発生した場合の対応は当然考えておきます。今回のデータ転送処理においては、もし差分があった場合は 移行元のデータが正となります。

そこで、移行元のデータを強制的に上書きすることで対応を行います。今回のデータ登録処理は冪等性が保証されている操作になるため、対象を絞った上で再度同じ転送処理を実施します。

Postgresql のテーブルには、計算されたハッシュ値の他に元となったデータが格納されています。そのため、差分として検出したデータを再度 JSON にして転送対象として登録すれば、転送方法で検討した Worker ソリューションがそのまま利用できます。

 S3 オブジェクトの情報を取得する先をクロスアカウントでのアクセスではなく、同一アカウント内の S3 Bucket へ変更し、再度 Worker による処理を再実行することで、抽出した差分のみ登録し直すことができます。

これでデータ完全性検証において差分が発生した場合の対応についても検討ができました。

データ完全性検証についてのまとめ

データ完全性検証の方法について検討した結果、我々は以下の方法を採用することにしました。

  • 移行元、移行先の DynamoDB テーブルから PITR を利用して S3 に Export した DynamoDB JSON をソースとして利用する
  • DynamoDB JSON を Postgresql のテーブルへ投入。データの md5 を計算してこれを hashId, データ本体と共に保持する
  • SQL で両者を比較して差分がないかをチェックする
  • 完全性検証で差分が発生した場合、対象を抽出しデータ再投入を実行する

データ件数チェック後、完全性検証まで来たらあとは走り切るしかないと考え、行動を判断するしきい値は検討していませんでした。

ここに関しては、ここまで来たらもうあとは完全一致するまで何度もリトライする以外思いつかなかったというのがあります。

ここまでのまとめ

前回は、転送方法の検証と対象の絞り込み、今回はデータ完全性の検証方法について記述しました。

もっとシンプルに書くつもりだったのですが、なぜこのような手法になったか、どのような検証を行っていたのか等を説明していたら、今回も長くなってしまいました。

  • 1100万件 x 3 テーブルの完全性チェック方法の検討 → 完了
  • (次回)全体のスケジュール概要
  • (次回)計画の実施と実行して初めて分かる現象とその対処について

まだもう少しつづきます。

脚注

  1. 前回よりかは短い
  2. 実は完全性検証の際にも別の方法で件数チェックは行っているので、複数の異なる方法で件数チェックを行っています。
  3. 最悪の場合
  4. また、この値については我々内部のみで定義したものであって、顧客となんらか合意を取って決定したものではありません。
  5. 万一差分があった際に素早く対象の抽出できるよう準備しています。