S3メタデータの活用方法を考えてみた(S3×Glue×Athenaでの構成)

# はじめに
こんにちは。株式会社ジールの[@yakisobapan](https://qiita.com/yakisobapan)です。

AWS re:Invent 2024で発表されたS3メタデータの活用方法を考えてみました。

# 目次
 - アップデート内容
 - アーキテクチャ
 - やってみた
 - 所感（ユースケース考察）


# アップデート内容

Amazon S3にオブジェクトが格納、変更、削除された際に生成されるメタデータを取得できるようになりました。
メタデータは、同タイミングで発表されたS3テーブルにて保持され、Athena等を利用してクエリすることが可能です。

>Amazon S3 メタデータ (プレビュー) の発表 — メタデータを管理する最も簡単かつ迅速な方法
（2024/12/03 What's New with AWS?より）

https://aws.amazon.com/jp/about-aws/whats-new/2024/12/amazon-s3-metadata-preview/

>Amazon S3 テーブルの発表 — 分析ワークロード向けに最適化されたフルマネージド型の Apache Iceberg テーブル
（2024/12/03 What's New with AWS?より）

https://aws.amazon.com/jp/about-aws/whats-new/2024/12/amazon-s3-tables-apache-iceberg-tables-analytics-workloads/

東京リージョンでも利用可能になっていました！

>Amazon S3 テーブルが新たに 5 つの AWS リージョンで利用可能になりました
（2025/1/17 What's New with AWS?より）

https://aws.amazon.com/about-aws/whats-new/2025/01/amazon-s3-tables-additional-aws-regions/

# アーキテクチャ
以下のような構成で考えてみました。
一般的なAWS上でのETL構成*に今回のアップデートを活用できそうです。

*S3をデータストアとして、データソース(csv等)をGlueジョブでParquetへ変換し格納、Athenaでクエリするといった構成

赤矢印が新機能のS3メタデータに関するデータフロー、参照フローです。
![構成図3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2737731/2c9c40cc-ed88-388e-513b-f1841f240428.png)


# やってみた

**事前準備**
上記構成を作成します。
- S3
・「テーブルバケット」形式でバケットを作成します。
　⇒末尾「参考」のドキュメントをご参照ください
・バケット配下にinフォルダ、outフォルダを作成します。
・データソースとなるcsvファイルをinフォルダに配置します。
- Glue
・各用途のData Catalogを作成します。
・ジョブを作成し、csvをParquetへ変換するPythonスクリプトを記述します。
```python:transform_csv_to_parquet
import sys
from awsglue.transforms import *
from awsglue.utils import getResolvedOptions
from pyspark.context import SparkContext
from awsglue.context import GlueContext
from awsglue.job import Job

# setup
args = getResolvedOptions(sys.argv, ['JOB_NAME'])
sc = SparkContext()
glueContext = GlueContext(sc)
spark = glueContext.spark_session
job = Job(glueContext)
job.init(args['JOB_NAME'], args)

# data read
input_path = "s3://yakisobapan-bucket-s3-metadata/in/bread/"
output_path = "s3://yakisobapan-bucket-s3-metadata/out/bread/"

datasource0 = glueContext.create_dynamic_frame.from_options(
    format_options={"withHeader": True, "separator": ","},
    connection_type="s3",
    format="csv",
    connection_options={"paths": [input_path]},
    transformation_ctx="datasource0"
)

# transform Parquet(Snappy)
datasink1 = glueContext.write_dynamic_frame.from_options(
    frame=datasource0,
    connection_type="s3",
    connection_options={"path": output_path},
    format="parquet",
    format_options={"compression": "SNAPPY"}
)

job.commit()
```

- Athena
・Data Catalogに対応したスキーマへテーブルを作成します。


**Glueジョブ実行時の挙動**
　inフォルダにあるcsvファイルをparquetへ変換してoutフォルダに出力するというスクリプトを実行します。

- ジョブ実行前のoutフォルダ
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2737731/df4133c3-c075-5f67-f51e-10561e927a73.png" width=100%>


- ジョブ実行後のoutフォルダ
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2737731/d8372c15-c49c-2062-8e68-490a7110990b.png" width=100%>


- Athenaでメタデータテーブルを確認してみる
作成されたオブジェクトに関するメタデータが1レコード追加されています。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2737731/79492c26-0d2b-2be8-bc76-cfeb59cf966c.png" width=100%>


**Glueジョブ実行時の挙動(2回目以降)**
- ジョブ実行後のoutフォルダ
追加で1ファイル作成されました。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2737731/2aabde21-71b6-b604-eaf3-3800e8ddff91.png" width=100%>


- Athenaでメタデータテーブルを確認してみる
連動してレコードが作成されていますね。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2737731/c825fc09-bf49-cc83-b0a8-0c8f4fdc953c.png" width=100%>


**オブジェクトに変更を行った際の挙動**
- 変更前
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2737731/2aabde21-71b6-b604-eaf3-3800e8ddff91.png" width=100%>


- 作成されたparquetファイルの名称を変更してみる
　ファイル名末尾を000→999へ変更します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2737731/ecfafc5d-48aa-2d4f-c498-94246874b85b.png" width=100%>


- 変更後
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2737731/3db16124-d013-3d74-ecc4-533eef24e4c4.png" width=100%>

- Athenaでメタデータテーブルを確認してみる
　変更が反映されていますね。
　変更後名称でオブジェクトが作成、変更前名称のオブジェクトは削除という動きになるようです。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2737731/9513845f-6778-020f-2758-4c5277a96e07.png" width=100%>

# 所感（ユースケース考察）
:::note info
ひとことで
S3メタデータを利用すると、Glueから出力されたparquertファイルの管理（出力日時や更新日時）精度が向上する
:::
今回のアップデート内容は、S3×Glue×AthenaのETL構成を行う際のS3オブジェクトの管理で活用できそうと考えました。

S3へのparquetファイル出力は、
Glue内でApache Sparkを利用した分散処理の結果、最適化されたファイルが出力されるため、ファイル名はpart-から始まるランダム文字列となります。

<br>

**・S3メタデータを使っていない場合**

例えば、ファイル名にタイムスタンプを付加して、出力日がわかるようにしたい場合、
追加で編集処理をスクリプトに加えるなどする必要がありました。

<br>

**ランダム文字列のファイルで運用していた場合、例えば、S3コンソールからのオブジェクト編集や移動によって、最終更新日時が変更されてしまいます。**

やってみた内の[オブジェクトに変更を行った際の挙動]のように
意図しないオブジェクト変更によって、最終更新日時が変更され「あれ、このファイルっていつ生成されたファイルだ？」となってしまっていました。

<br>

**・S3メタデータを使った場合**

S3に出力されたparquetファイルがいつ作成されたか、テーブル形式で保持できるようになりました。
**これにより、仮にファイルが変更された場合でも情報を追うことができ、対象ファイルの作成日時を特定することができます。**

参照可能なメタデータが拡充されていけば、information schemaのように利用することもできそうな気がしました。
Athenaがクエリエディターにとどまらず、DBの代替となる日も近い、、？


# 参考
・S3メタデータテーブルの構成、設定手順

https://docs.aws.amazon.com/AmazonS3/latest/userguide/metadata-tables-configuring.html

・オブジェクトメタデータの種類

https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingMetadata.html#SysMetadata