AppFlowのGlueデータカタログ統合機能を検証してみた

# はじめに
この記事は[BeeX Advent Calendar 2022](https://qiita.com/advent-calendar/2022/beex)の7日目の記事です。
え？今何日目か数えてみろよって？(投稿日12/9)
インフル罹って寝込んでましたごめんなさい

それはともかく、先月の話ですがAmazon AppFlowでGlue DataCatalogとの統合をサポートするアップデートが発表されてました。
このリリースが出る前までは、一度AppFlowでS3に出力した後に別途Glue Crawlerを動かさないとAthenaなどからクエリできなかったのが、このリリースが出ることでGlue Crawlerが不要になりましたので、検証がてら試してみることにします。

https://aws.amazon.com/jp/about-aws/whats-new/2022/11/amazon-appflow-aws-glue-data-catalog-integration-provides-enhanced-data-preparation/

# 検証内容
## 前提条件
個人的都合から一番使いやすい以下のデータで検証を行いました。
Salesforce環境及び出力先のS3バケットについては事前に作成済みです。

* データ送信元：Salesforce
* データ送信先：S3

## 通常実行
まずはコンソールの指示に従って、フローを作成・実行させてみます。

### フロー作成・実行
Salesforceオブジェクトはデフォルトで存在している「取引先」を指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/5edbe8f9-a842-c782-939b-6f715769da2d.png)

送信先にS3を指定すると「AWS Glue データカタログの設定」が表示されるようになっています。
ここをチェック入れると

- ユーザーロール
- データベース(Glue)
- テーブル名のプレフィックス

を入力する項目が出てきますので、それぞれ必要な情報を入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/e4a97495-e8b5-3438-9b62-852cac43ba23.png)

ユーザーロールの権限については以下のURLにあるポリシーを参考にしました。
https://docs.aws.amazon.com/appflow/latest/userguide/security_iam_id-based-policy-examples.html

Glue DataCatalogのデータベース名を指定する箇所もありますが、AppFlowの画面上から作成することも可能になっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/7174a9f2-77a4-50ab-4aff-179dea8cd671.png)

テーブル名にはプレフィックスを付けることができます。
といいますか、このプレフィックスは必須項目なので、何かしらの名前を先頭に付ける必要があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/c3ab1b4e-3966-5e75-b4ee-23a0a08c6dbb.png)

今回はフィールドマッピングはさほど重要じゃないので、その下の「パーティションと集約の設定」を開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/9df74781-dc27-c581-aa8d-c47aa41bc0d0.png)

細かくパーティションの設定は可能ですが、それは後ほど試すのでここでは設定を変更せずに次に行きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/07a1aa6d-d441-f202-c5f9-880007eabeec.png)

以降は特に設定するものもないので、そのまま作成して実行してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/63dc12ee-0ec0-0591-b030-4a893a87a127.png)

Glue Crawlerが裏で動いているからか前よりも時間がかかってる気がしますが、正常終了すると以下のように表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/1bd04fba-8eb2-afe9-0332-1798b256d6ba.png)

### 結果確認
S3バケットから出力を確認してみます。
「S3バケット名＞指定したプレフィックス＞フロー名＞スキーマバージョン＞実行ID」配下にファイルが出力されており、拡張子はないですが、ダウンロードして開いてみるとCSVでデータが入っていることが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/77925aef-059e-a3bc-8717-3bc21bd50155.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/f7e722f8-006f-2651-1e38-ebd661014c6b.png)

GlueのData Catalogから確認してみます。
ちゃんとテーブルは登録されているようですが、1回しか実行していないのに何故かテーブルは2つ作成されていました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/ddee9a60-3dfc-415e-ff18-78b1e9bde630.png)

比較してみましたが、名前以外は全く同じで、同じデータソースをクローリングしているように見えます。
この後試してみればわかることだとは思いますが、恐らくlatestテーブルは常に最新のデータの情報が登録されるようになっており、古いデータを見たい場合はlatestが付いていないテーブルを直接参照することができるようになっているのだと思われます。
(もしかしてデータベース1つに対してlatestは1つしか持てないとかある...？)

- latestが付いているテーブル
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/425e7ed0-bae4-67fd-eafd-090772486873.png)

- latestが付いていないテーブル
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/ad25798e-5670-75ce-9b23-1a308b3981ba.png)

Athenaからもクエリすることができました。
※ただ「select *」すると一部カラムでエラーになるので、きれいに変換できていないものもあるのかもしれません
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/d3b1eedd-1208-7853-436c-60dc98ab418b.png)



## パーティション指定実行
次はパーティションを作成するように設定して実行してみます。
また、フローも新規で作成してみて、同一データベース内にlatestを複数持つことができるのかも合わせて確認します。
尚、連携元のオブジェクトは同じものを使用します。

### フロー作成・実行
Glue Data Catalogの設定は先ほどと同じように設定します。
(そういえばプレフィクスの有効文字にハイフンないけどエラーチェックされてない...バグかな？)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/a6afcf3a-1954-7a15-6084-026515099c3e.png)

先ほどデフォルトのまま設定したパーティション設定で、送信先フィールドにチェックして、パーティションキーとして設定するフィールドを選択します。
今回はレコードの作成日時を指定したいと思いますが、以下の「作成日」フィールドは時分秒込み、「作成年月日」フィールドは時分秒なしとなっており、**時分秒が含まれているフィールドが選択された場合エラーとなる現象**を既に確認しています。
そのため、時分秒なしの「作成年月日」フィールドのみパーティションに設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/39206667-70bc-ef16-6fab-151741c22746.png)

※以下のように**時分秒に含まれるコロン( : )が原因**で実行エラーが発生していました。
　メッセージ内容からAWS側も想定していないエラーのような気もしますが、、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/c1dd99a1-e89b-caab-cded-d85f0cd161ab.png)

集約の設定は複数のファイルに集約するように設定します。
尚、ターゲットファイルサイズは128MB~600MBの範囲で決まったサイズを指定することができますが、理由として[こちらのページ](https://aws.amazon.com/jp/blogs/news/top-10-performance-tuning-tips-for-amazon-athena/)にあるようにファイルサイズが小さいことで発生する余分な時間を発生させないためのようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/e327f506-d760-33a8-c11a-bafdebe76331.png)

正常終了したので結果を見てみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/65ef35e8-bd9f-d495-da87-d4739031480a.png)

### 結果確認
S3バケットを確認してみるとちゃんとパーティションとして認識される形でフォルダが作成されていました。
作成年月日を入れていたデータは1レコードだけだったので、想定通り出力されているようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/f98209a9-fa56-7688-5c43-c5245b469f5e.png)

続いてGlueのData Catalogから確認してみました。
latestが付いているテーブルが複数あることから、latestテーブルはAppFlowのフロー毎に定義されるようになっているみたいです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/dfc21bab-d003-a1ef-3c76-059a397ba6ce.png)

Athenaからも正常にクエリすることができました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/5bf25626-7bb3-9b13-cc75-3582ae12425d.png)


## おまけ1：同じフローを複数回実行するとGlueテーブルはどうなるのか？

試しに同じフローを複数回実行してみると、新しい実行IDのテーブルが1つ増えてました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/dddfec97-cea4-cec2-b3ac-2214b90aa347.png)

latest付きのテーブルを見てみると、LocationのS3パスが最後に実行された実行IDを指していたので、1回目のGlue結果のところで記載した以下の内容は正しそうです。

>恐らくlatestテーブルは常に最新のデータの情報が登録されるようになっており、古いデータを見たい場合はlatestが付いていないテーブルを直接参照することができるようになっているのだと思われます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/d3ec2f43-1682-ac5d-be6c-d355916b2fbf.png)


## おまけ2：実行IDを指定しないとき、S3パスとGlueのテーブル名はどうなるのか？
以下の設定で実行IDのチェックを外してフローを実行してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/233077bc-4e3f-ceee-d0ae-dcf8edff85e3.png)

S3パスからは実行IDがなくなりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/da0b7727-1cd6-aac3-c252-a3012eb23596.png)

Glueのテーブル名からも実行IDがなくなりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/87989f74-5344-fe5c-6221-8234907e2268.png)

この場合、実行する度にクローリングされるS3パスが変わらないため、フローを実行する度に実行IDが含まれたGlueのテーブルが増えていくということはなくなります。
ただ、AppFlowフロー側で差分連携をするように設定されておらず、毎実行で全データを連携するようになっている場合、S3バケット上にはファイル上書きではなく別名で保存されるため、クローリングされた先のGlue/Athenaでクエリする際に同じデータが重複表示されるため注意が必要です。(以下同じデータが別ファイルで格納される例)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/254e4c07-71c0-4e21-82e7-27bb225c74cd.png)

実行ID以外にもGlueのテーブル名にはスキーマバージョン名(sv1やsv2など)も含まれており、AppFlowのフローのフィールドマッピングを更新した場合などに自動でカウントアップされるため、その際はGlueのテーブルが増えていくような動きになっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/8c2b4572-9d6b-dbf2-ff77-bb97b793282f.png)


# おわりに
最初このアップデートを見たときはかなりいいアップデートが来たんじゃないかと思ってましたが、実際使ってみるとちょっと癖がありそうなので、利用ケースが少し限定されそうな気がしました。

マネージドサービスなので当たり前なのですが、AppFlow内の変換処理の内容がわからないので、AppFlowで変換されたけどGlue/Athena側でカラムを正しく認識できない、といったことがある可能性があります。(記事には書いてないですがありました)
簡単にデータ連携できると思ったら実はできなかった、ということがありそうなので、過去に検証した、[AppFlow出力データをGlueDataBrewで変換](https://www.capybara-engineer.com/entry/2021/12/03/115922)する方法なども含めて構成を検討できると良いのではないかなと思いました。

ちなみに、GlueDataBrewで変換した際もフィールド値をパーティション設定することができるのですが、そちらでは時分秒の間のコロン( : )が原因で失敗するということはなく、エラーが出ないように自動で変換されてました。
仕組み的には近い気がするのけど、ロジックは違うみたい。。