最初に結論
ハマったこと
AWS Glueの増分クローラーを使うときはデータソース(S3)にデータがない状態で初回実行してしまうと2回目以降のクロールでもスキーマ作成されずパーティション更新もされない。
回避するためには?
- 初回実行前にデータを入れておく
- 増分クローラーを使うのをやめる
- テーブルやスキーマ作成部分をGlueに任せるのをやめる
背景
DBからS3バケットにデータを流し込んでAthena検索できる環境を構築するためにAWS GlueのCrawlerを使ってデータカタログテーブル作成・更新をしていました。
Crawlerを使えばパーティションキーをはじめとするテーブル情報の作成と更新を自動でやってくれるのでAthena検索をスムーズに行えるわけです。
ただ、Crawlerも万能ではないため性質を理解した上で使う必要があります。
クローラーの仕組み
クローラーを実行すると、クローラーは以下のアクションを使用してデータストアを調査します。
・生データの形式、スキーマ、および関連プロパティを確認するためにデータを分類する – カスタム分類子を作成して分類の結果を設定できます。
・データをテーブルまたはパーティションにグループ化する – データはクローラーのヒューリスティックに基づいてグループ化されます。
・メタデータをデータカタログに書き込む – クローラーでテーブルやパーティションを追加、更新、削除する方法を設定できます。
分類子
メタデータテーブル(テーブルのスキーマも含む)を定義する際にデータの形式を評価してスキーマを推測してくれるのが分類子です。
クローラーが実行されると、最初にデータストアを正しく認識した分類子が、テーブルのスキーマを作成するために使用されます。組み込みでの提供もありますし、カスタムすることもできます。
パーティション
クロール対象のS3バケットパスはあらかじめ指定しておくのですが、この配下にあるオブジェクトのスキーマのおおよそが類似している場合、クローラーはテーブルを個別で作成せずにパーティションを作成するわけです。
仮にパーティション毎に微妙にデータ型が異なっているにも関わらず、クローラーの判断でテーブルを分けなかった場合どうなるのかというと、スキーマの不一致でAthena検索がうまくできないことがあります。
Athena がクエリを実行するときは、テーブルのスキーマと、クエリに必要なパーティションのスキーマを検証します。この検証では、列のデータ型を順に比較し、重複する列のデータ型が一致することを確認します。これにより、テーブルの中間で列が追加/削除されるなどの予期しないオペレーションが防止されます。パーティションのスキーマがテーブルのスキーマと異なることを Athena が検知した場合、Athena はクエリを処理できない可能性があり、HIVE_PARTITION_SCHEMA_MISMATCH で失敗します。
こうなるとS3のデータを削除して、MSCK REPAIR
を実行してパーティションを作り直したりと色々面倒なことが起こります。
料金
課金はクロール対象のデータ量によって(DPUが決まり、そちらに基づいて)決まります。
AWS Glue データカタログにデータを取り込む場合は、時間あたりの料金がかかります。クローラーの実行に使用された DPU (Data Processing Unit) の数に基づいて時間あたりの課金が発生します。
コストを抑えようと思うとすでにクロール済みの部分はなるべくスルーしたいわけですが、そこで使われるのが増分クローラーです。
増分クローラーとは
クローラーには新しいパーティションを追加するオプションがあり、安定したテーブルスキーマを持つ増分データセットのクロールが速くなります。典型的なユースケースは、スケジュールされたクローラーで、クロールごとに新しいパーティションが追加されます。このオプションをオンにすると、最初にターゲットデータセットで完全なクロールを実行し、クローラーが初期スキーマとパーティション構造を記録できるようになります。クロールの再実行時、スキーマに互換性がある場合にのみ、新しいパーティションが既存のテーブルに追加されます。最初のクロール実行後に、スキーマの変更は行われず、Data Catalog に新しいテーブルを追加することはありません。
増分クローラーを使うことで文字通り、増えた分だけをクロール対象とするので処理時間とコストを抑えることが可能です。
ただし、注意点があります。(ここが本題の話)
完全なクロールが実行されて初回実行時にのみ初期スキーマとパーティション構造を記録できるようになります。以降はスキーマに互換性がある場合にのみ、新しいパーティションが既存のテーブルに追加されます。
最初にターゲットデータセットで完全なクロールを実行し、クローラーが初期スキーマとパーティション構造を記録できるようになります。
つまり、最初の1回を実行するときにデータがなければ以降のクロールは無意味ということです。テーブル自体は作成されますが、スキーマを作成しようにもデータがないので、空のテーブルが作られます。そうなったらもう終わりで、仮に2回目実行前にデータを入れたとしてもスキーマが作られることもないですし、パーティションも作られません。
この問題は回避できなさそうで初回実行前にデータを入れておくしかなさそうです。