Amazon S3 Tables でタイムトラベルクエリを試してみた
こんにちは!アノテーション AWS テクニカルサポートチームの大高です。
先日、Amazon S3 Tables のテーブル作成と簡単なクエリを試してみました。
通常のクエリは問題なさそうでしたが、今回は Apache Iceberg テーブルの特徴である「タイムトラベルクエリ」を試してみたいと思います。
Amazon S3 Tables の「タイムトラベルクエリ」とは
Amazon S3 Tables は、Apache Iceberg サポートが組み込まれています。
この Apache Iceberg テーブルでは、S3 オブジェクトのバージョン管理されたマニフェストを維持しており、以前のバージョンのマニフェストを、タイムトラベルおよびバージョントラベルのクエリに使用できます。
つまりは、テーブルの過去のバージョンの状態に対してクエリができる、ということですね。
やってみた
今回は、下記のドキュメントを参照しながら、実際にタイムトラベルクエリを試してみます。
テーブルの履歴を確認する
タイムトラベルクエリでは、テーブルの特定の日時を指定する必要があります。
そのため、まずは Athena で以下のクエリを実行して、テーブルの履歴を確認してみます。
SELECT * FROM "sample_table$history" limit 10;
# | made_current_at | snapshot_id | parent_id | is_current_ancestor |
---|---|---|---|---|
1 | 2025-10-10 09:35:46.194 UTC | 4900786887601180495 | true | |
2 | 2025-10-10 09:35:54.781 UTC | 5469666753384997262 | 4900786887601180495 | true |
3 | 2025-10-10 09:36:03.297 UTC | 7919870596757976173 | 5469666753384997262 | true |
これは、3 件のレコードを 1 件ずつ INSERT した際のテーブル履歴となっています。
今回は、この中で一番古い 1 件だけレコードを入れた際の日時 2025-10-10 09:35:46.194 UTC
を使ってみます。
タイムトラベルクエリを実行してみる
それでは、以下のクエリを実行してみます。
SELECT * FROM "sample_table" FOR TIMESTAMP AS OF TIMESTAMP '2025-10-10 09:35:46.194 UTC';
# | id | name | value |
---|---|---|---|
1 | 1 | name1 | 1 |
想定どおり、当時の 1 件だけレコードを入れたテーブルから SELECT ができました!
(おまけ) CloudShell に導入した Spark Shell からテーブルロールバックを試してみた
タイムトラベルクエリで取得した過去の状態へのテーブルロールバックも試してみました。
CALL s3tablesbucket.system.rollback_to_snapshot('sample_namespace.sample_table', 4900786887601180495');
しかし、Athena では以下のエラーが発生してプロシージャの CALL はできませんでした。
line 1:1: mismatched input 'CALL' expecting {'(', 'SELECT', 'FROM', 'ADD', 'DESC', 'WITH', 'VALUES', 'CREATE', 'TABLE', 'INSERT', 'DELETE', 'DESCRIBE', 'EXPLAIN', 'SHOW', 'USE', 'DROP', 'ALTER', 'MAP', 'SET', 'RESET', 'START', 'COMMIT', 'ROLLBACK', 'REDUCE', 'REFRESH', 'CLEAR', 'CACHE', 'UNCACHE', 'DFS', 'TRUNCATE', 'ANALYZE', 'LIST', 'REVOKE', 'GRANT', 'LOCK', 'UNLOCK', 'MSCK', 'EXPORT', 'IMPORT', 'LOAD'}
以下に記載のあるとおり、Apache Spark 環境が必要なようです。
注: Athena クエリエディターではなく、AWS Glue や Amazon EMR などの Apache Spark 環境でテーブルをロールバックする必要があります。
以下の記事を参考に、CloudShell で Spark Shell が使えるようにしてみます。
- Java の確認
CloudShell では標準で Java が利用できるはずなので、確認します。
$ java -version
openjdk version "21.0.8" 2025-07-15 LTS
OpenJDK Runtime Environment Corretto-21.0.8.9.1 (build 21.0.8+9-LTS)
OpenJDK 64-Bit Server VM Corretto-21.0.8.9.1 (build 21.0.8+9-LTS, mixed mode, sharing)
問題なく、Amazon Corretto 21 が導入済みですね。
- Apache Spark のダウンロードとインストール
以下のとおり、ダウンロードとインストールを行います。なお、ダウンロードするバージョンは 3.5.7 とします。
$ cd /tmp
$ wget https://dlcdn.apache.org/spark/spark-3.5.7/spark-3.5.7-bin-hadoop3.tgz
$ tar -xzf spark-3.5.7-bin-hadoop3.tgz
なお、最初はホームディレクトリに展開しようとしましたが、ストレージ不足となってしまったため、一時ストレージである /tmp
を利用しています。
- 環境変数の設定
インストールしたパスを環境変数に設定して反映させます。
$ echo 'export SPARK_HOME=/tmp/spark-3.5.7-bin-hadoop3' >> ~/.bashrc
$ echo 'export PATH=$PATH:$SPARK_HOME/bin' >> ~/.bashrc
$ source ~/.bashrc
- 動作確認
$ spark-shell --version
Welcome to
____ __
/ __/__ ___ _____/ /__
_\ \/ _ \/ _ `/ __/ '_/
/___/ .__/\_,_/_/ /_/\_\ version 3.5.7
/_/
Using Scala version 2.12.18, OpenJDK 64-Bit Server VM, 21.0.8
Branch HEAD
Compiled by user runner on 2025-09-17T20:37:30Z
Revision ed00d046951a7ecda6429accd3b9c5b2dc792b65
Url https://github.com/apache/spark
Type --help for more information.
これで、無事に spark-shell が利用できるようになりましたので、以下のドキュメントを参考に起動してみましょう。
--conf spark.sql.catalog.s3tablesbucket.warehouse=
で指定する ARN は自分の S3 Tables のバケットに書き換えます。
また、 --packages
には、org.apache.iceberg:iceberg-spark-runtime-3.5_2.12:1.5.0
と software.amazon.awssdk:bundle:2.20.0
を追加で指定しています。
なお、ストレージ不足を防ぐため ivy のキャッシュも spark.jars.ivy=/tmp/.ivy2
として /tmp
配下を指定しています。
$ spark-shell \
--packages software.amazon.s3tables:s3-tables-catalog-for-iceberg-runtime:0.1.3,org.apache.iceberg:iceberg-spark-runtime-3.5_2.12:1.5.0,software.amazon.awssdk:bundle:2.20.0 \
--conf spark.jars.ivy=/tmp/.ivy2 \
--conf spark.sql.catalog.s3tablesbucket=org.apache.iceberg.spark.SparkCatalog \
--conf spark.sql.catalog.s3tablesbucket.catalog-impl=software.amazon.s3tables.iceberg.S3TablesCatalog \
--conf spark.sql.catalog.s3tablesbucket.warehouse=arn:aws:s3tables:ap-northeast-1:XXXXXXXXXXXX:bucket/ootaka-sample-s3-table-backet \
--conf spark.sql.defaultCatalog=s3tablesbucket \
--conf spark.sql.extensions=org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions
起動できたら、試しに namespace を確認します。
scala> spark.sql("SHOW NAMESPACES").show()
+----------------+
| namespace|
+----------------+
|sample_namespace|
+----------------+
想定どおり、表示されました。
では、本命のテーブルロールバックをしてみます。以下では 1 件のレコードが入っていた時点のスナップショット ID を指定しています。
spark.sql("CALL s3tablesbucket.system.rollback_to_snapshot('sample_namespace.sample_table', 7827017876549943173)").show()
+--------------------+-------------------+
|previous_snapshot_id|current_snapshot_id|
+--------------------+-------------------+
| 7511407681386237430|5982459232010435748|
+--------------------+-------------------+
この状態で下記のように SELECT してみましたが、なぜか 0 件になってしまいました。
scala> spark.sql("SELECT * FROM sample_namespace.sample_table").show()
+---+----+-----+
| id|name|value|
+---+----+-----+
+---+----+-----+
なお、タイムスタンプを利用した下記のプロシージャも利用しましたが同様でした。
spark.sql("CALL s3tablesbucket.system.rollback_to_timestamp('sample_namespace.sample_table', TIMESTAMP '2025-10-16 09:32:47.061')").show()
なんらかの設定不足が原因でメタデータの読み込みが失敗しているように思われましたが、現時点では明確な原因を掴むことができませんでした。
今後なにか分かったら追記したいと思います。
まとめ
以上、Amazon S3 Tables のタイムトラベルクエリを試してみました。
「誤ってレコードを DELETE してしまった」などの場合には、タイムトラベルクエリで Athena 上から簡単に過去データを参照できるので助かりそうですね。
どなたかのお役に立てば幸いです。それでは!
参考