![[アップデート] AWS Glue でシステム管理型の Iceberg マテリアライズドビューが利用可能になったので、通常のマテリアライズドビューと書き込みを比べてみた](https://devio2024-media.developers.io/image/upload/f_auto,q_auto,w_3840/f_auto,q_auto/v1790870377/eyecatch/kjl12txf2xakoko9pzvo.png)
[アップデート] AWS Glue でシステム管理型の Iceberg マテリアライズドビューが利用可能になったので、通常のマテリアライズドビューと書き込みを比べてみた
クラウド事業統括本部の石川です。AWS Glue の Apache Iceberg マテリアライズドビューで、データと定義への書き込みを AWS Glue だけに限定する「システム管理型」が利用可能になりました。同じ定義の通常のマテリアライズドビューとシステム管理型を並べて作り、Spark、Amazon Athena、AWS Glue API、Amazon S3 Tables API から書き込みを試して結果を比べてみました。
AWS Glue のマテリアライズドビュー(以降、MV)は、SQL で定義したクエリの結果をあらかじめ計算し、Apache Iceberg テーブルとして保存する機能です。結果は AWS Glue Data Catalog に登録された Iceberg テーブルなので、Amazon Athena や Amazon EMR など Iceberg に対応したエンジンから読み取れます。
一方で、結果がオープンな Iceberg テーブルであるため、書き込み権限を持つユーザーは、カタログ経由でも、Amazon S3 上のファイルへの直接書き込みでも MV の中身を変更できます。システム管理型 MV は、MV のデータと定義を書き込める主体を AWS Glue だけに限定します。テーブルやその基盤となる S3 ファイルへの書き込み権限を誰が持っていても、計算結果は AWS Glue が計算した状態のまま保たれます。
システム管理型 MV は、Apache Iceberg MV がサポートされているすべてのリージョンで利用できます。前提となる AWS Glue 6.0 は 2026 年 8 月に一般提供が始まっています。
システム管理型マテリアライズドビューとは
AWS Glue デベロッパーガイドでは、システム管理型 MV を「AWS Glue だけが書き込める MV」と説明しています。AWS Glue がデータと定義の両方を書き込み、他のエンジンやユーザーは変更できません。保存先は自分のアカウントの Amazon S3 Tables バケットで、形式は標準の Apache Iceberg テーブルです。
利用の要件は次のとおりです。
- AWS Glue 6.0 以降
- Spark セッションの設定
spark.sql.mv.managed.enabledをtrueにする CREATE MANAGED MATERIALIZED VIEWのようにMANAGEDキーワードを付けて作成する- 保存先は Amazon S3 Tables のみ(S3 汎用バケットは非対応)
作成時、AWS Glue は S3 Tables に空のテーブルを作り、その後で定義クエリを実行してデータを入れます。CREATE MANAGED MATERIALIZED VIEW 文は AWS Glue が作成を終えるまで戻らないため、通常の MV より時間がかかる場合があります。作成の進捗は、GetTable API に IncludeStatusDetails=true を指定すると確認できます。
書き込み保護の対象は、MV のデータと、スキーマや保存場所などのメタデータです。ドキュメントには、次の経路からの変更が拒否されると書かれています。
- 他のクエリエンジン
- AWS Glue の
UpdateTableAPI の直接呼び出し - 基盤の Apache Iceberg テーブルへの直接書き込み
一方、次の操作は引き続き行えます。
- MV の更新(REFRESH)。AWS Glue が更新を実行して結果を書き込む
- 更新スケジュールとテーブルプロパティの変更
- MV の削除(削除権限がある場合)
スキーマ進化はサポートされていません。SQL 定義を変えるには、MV を削除して作り直します。
通常の MV との違いは次のとおりです。
| 項目 | 通常の MV | システム管理型 MV |
|---|---|---|
| 必要な AWS Glue バージョン | 5.1 以降 | 6.0 以降 |
| 保存先 | S3 Tables または S3 汎用バケット | S3 Tables のみ |
| 作成の構文 | CREATE MATERIALIZED VIEW |
CREATE MANAGED MATERIALIZED VIEW |
| データと定義を書き込める主体 | 書き込み権限を持つユーザーとエンジン | AWS Glue のみ |
| スキーマ進化 | - | 非対応(削除して作り直す) |
やってみた
ややこしい検証でしたので、最初に結論をお伝えします。概ね期待通りの結果でした。
同じ定義の通常 MV とシステム管理型 MV を同じ S3 Tables の namespace に作成し、同じ書き込み操作を両方に実行して結果を比べました。
最初に結論: 検証結果一覧
| 操作 | 実行したロール | 通常 MV | システム管理型 MV |
|---|---|---|---|
Spark INSERT INTO |
ジョブロール | 成功 | 失敗(S3 403) |
Spark DataFrame append |
ジョブロール | 成功 | 失敗(S3 403) |
Spark DELETE |
ジョブロール | 成功 | 失敗(S3 403) |
Spark ALTER TABLE ADD COLUMNS |
ジョブロール | 実施せず | 拒否 |
Athena SELECT |
検証者 | 成功 | 成功 |
Athena INSERT INTO |
検証者 | 成功 | 失敗(S3 403) |
UpdateTable(IsManaged を false に変更) |
検証者 | - | 拒否 |
UpdateTable(ViewDefinition を含めない) |
検証者 | 成功を返したが定義は残った | 拒否 |
UpdateTable(定義 SQL の書き換え) |
検証者 | 拒否 | 拒否 |
S3 Tables UpdateTableMetadataLocation |
検証者 | 成功(その後、整合性チェックで読み取り不可) | 拒否(明示的な Deny) |
REFRESH ... FULL |
ジョブロール | 失敗(整合性チェック) | 成功(456.5 秒) |
SET TBLPROPERTIES |
ジョブロール | 実施せず | 成功 |
ADD SCHEDULE / DROP SCHEDULE |
ジョブロール | 実施せず | 成功(DROP 後もスケジュールが残った) |
DROP MATERIALIZED VIEW |
ジョブロール | 成功 | 成功 |
前提条件
- リージョン: アジアパシフィック(東京)ap-northeast-1
- AWS Glue 6.0(Spark 4.1.1-amzn-0)、ワーカー G.1X × 2 の Spark ジョブ
- Amazon Athena(エンジンバージョン 3、primary ワークグループ)
- Amazon S3 Tables と AWS Glue Data Catalog の統合(
s3tablescatalog)が設定済み
書き込みを試した主体は次の 2 つです。
- Glue ジョブのロール(
blog-mv-e603a380)。MV を作成したロール(定義者ロール)で、今回の S3 Tables バケットとそのテーブルにs3tables:*を許可しています - 検証者のロール(
cm-user)。AWS CLI と Athena の実行に使い、検証用リソースの作成と削除もこのロールで行いました。Lake Formation のデータレイク管理者に設定されています
検証用リソースを作成する
ソーステーブル用の S3 汎用バケットと Glue データベース、MV の保存先となる S3 Tables バケットと namespace を作成します。
% export AWS_REGION=ap-northeast-1
% export ACCOUNT_ID=123456789012
% export BUCKET=blog-mv-e603a380
% export TABLE_BUCKET=blog-mv-e603a380
% export SRC_DB=blog_mv_src_e603a380
% export S3T_CATALOG=${ACCOUNT_ID}:s3tablescatalog/${TABLE_BUCKET}
% export TB_ARN=arn:aws:s3tables:${AWS_REGION}:${ACCOUNT_ID}:bucket/${TABLE_BUCKET}
% aws s3api create-bucket --bucket $BUCKET \
--create-bucket-configuration LocationConstraint=$AWS_REGION
aws s3tables create-table-bucket --name $TABLE_BUCKET
aws s3tables create-namespace --table-bucket-arn $TB_ARN --namespace analytics
aws glue create-database --database-input \
"{\"Name\":\"${SRC_DB}\",\"LocationUri\":\"s3://${BUCKET}/warehouse/${SRC_DB}.db\"}"
{
"arn": "arn:aws:s3tables:ap-northeast-1:123456789012:bucket/blog-mv-e603a380"
}
{
"tableBucketARN": "arn:aws:s3tables:ap-northeast-1:123456789012:bucket/blog-mv-e603a380",
"namespace": [
"analytics"
]
}
S3 Tables の統合を設定済みのため、作成したテーブルバケットは Glue Data Catalog に s3tablescatalog/<テーブルバケット名> というカタログとして現れます。
% aws glue get-databases --catalog-id $S3T_CATALOG \
--query 'DatabaseList[].{Name:Name,CatalogId:CatalogId}' --output table
-----------------------------------------------------------------------
| GetDatabases |
+--------------------------------------------------------+------------+
| CatalogId | Name |
+--------------------------------------------------------+------------+
| 123456789012:s3tablescatalog/blog-mv-e603a380 | analytics |
+--------------------------------------------------------+------------+
Glue ジョブ用の IAM ロールを作成する
Glue ジョブのロールを作成します。MV を自動更新するには、AWS Glue がこのロールを引き受けられるように、信頼ポリシーで glue.amazonaws.com を許可します。
% aws iam create-role --role-name blog-mv-e603a380 \
--assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [{"Effect": "Allow", "Principal": {"Service": "glue.amazonaws.com"}, "Action": "sts:AssumeRole"}]
}'
権限は、ドキュメントの「IAM permissions for AWS Glue jobs」に記載された内容に、自分自身への iam:PassRole と、今回の S3 Tables バケットへの s3tables:* を加えました。
{
"Version": "2012-10-17",
"Statement": [
{"Sid": "GlueCatalog", "Effect": "Allow", "Action": [
"glue:GetCatalog", "glue:GetCatalogs", "glue:GetTable", "glue:GetTables",
"glue:CreateTable", "glue:UpdateTable", "glue:DeleteTable",
"glue:GetDatabase", "glue:GetDatabases", "cloudwatch:PutMetricData"], "Resource": "*"},
{"Sid": "S3Objects", "Effect": "Allow", "Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
"Resource": ["arn:aws:s3:::blog-mv-e603a380/*"]},
{"Sid": "S3List", "Effect": "Allow", "Action": ["s3:ListBucket"], "Resource": ["arn:aws:s3:::blog-mv-e603a380"]},
{"Sid": "Logs", "Effect": "Allow", "Action": ["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"],
"Resource": ["arn:aws:logs:*:*:*:/aws-glue/*"]},
{"Sid": "PassSelf", "Effect": "Allow", "Action": ["iam:PassRole"],
"Resource": ["arn:aws:iam::123456789012:role/blog-mv-e603a380"]},
{"Sid": "S3TablesBucket", "Effect": "Allow", "Action": ["s3tables:*"],
"Resource": ["arn:aws:s3tables:ap-northeast-1:123456789012:bucket/blog-mv-e603a380",
"arn:aws:s3tables:ap-northeast-1:123456789012:bucket/blog-mv-e603a380/table/*"]},
{"Sid": "S3TablesList", "Effect": "Allow", "Action": ["s3tables:ListTableBuckets"], "Resource": "*"},
{"Sid": "LF", "Effect": "Allow", "Action": ["lakeformation:GetDataAccess"], "Resource": "*"}
]
}
% aws iam put-role-policy --role-name blog-mv-e603a380 \
--policy-name mv-test --policy-document file://policy.json
後述のとおり、このポリシーでは REFRESH が失敗し、権限を 1 つ追加しました。
Glue 6.0 ジョブを作成する
Glue 6.0 の Spark ジョブを作成します。--conf には、ドキュメントの「For S3 Tables buckets」の設定に spark.sql.mv.managed.enabled=true を加えています。ソーステーブルは glue_catalog(Glue Data Catalog の既定カタログ)、MV は s3t_catalog(s3tablescatalog/<テーブルバケット名>)で扱います。
{
"Name": "blog-mv-e603a380",
"Role": "arn:aws:iam::123456789012:role/blog-mv-e603a380",
"Command": {"Name": "glueetl", "ScriptLocation": "s3://blog-mv-e603a380/scripts/mv_test.py", "PythonVersion": "3"},
"DefaultArguments": {
"--enable-glue-datacatalog": "true",
"--job-language": "python",
"--src_db": "blog_mv_src_e603a380",
"--ns": "analytics",
"--phase": "phase1",
"--conf": "spark.sql.extensions=org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions --conf spark.sql.catalog.glue_catalog=org.apache.iceberg.spark.SparkCatalog --conf spark.sql.catalog.glue_catalog.type=glue --conf spark.sql.catalog.glue_catalog.warehouse=s3://blog-mv-e603a380/warehouse --conf spark.sql.catalog.glue_catalog.glue.region=ap-northeast-1 --conf spark.sql.catalog.glue_catalog.glue.id=123456789012 --conf spark.sql.catalog.glue_catalog.glue.account-id=123456789012 --conf spark.sql.catalog.s3t_catalog=org.apache.iceberg.spark.SparkCatalog --conf spark.sql.catalog.s3t_catalog.type=glue --conf spark.sql.catalog.s3t_catalog.glue.id=123456789012:s3tablescatalog/blog-mv-e603a380 --conf spark.sql.catalog.s3t_catalog.glue.account-id=123456789012 --conf spark.sql.catalog.s3t_catalog.warehouse=s3://blog-mv-e603a380/mv-warehouse --conf spark.sql.catalog.s3t_catalog.glue.region=ap-northeast-1 --conf spark.sql.defaultCatalog=s3t_catalog --conf spark.sql.optimizer.answerQueriesWithMVs.enabled=true --conf spark.sql.materializedViews.metadataCache.enabled=true --conf spark.sql.mv.managed.enabled=true"
},
"GlueVersion": "6.0",
"WorkerType": "G.1X",
"NumberOfWorkers": 2,
"Timeout": 30
}
% aws glue create-job --cli-input-json file://job.json
ジョブスクリプトは、--phase 引数に応じて SQL を順に実行し、各ステートメントの成否、所要時間、結果の行を 1 行の JSON にして標準出力に書きます。1 つのステートメントが失敗しても、後続のステートメントは実行を続けます。結果は CloudWatch Logs のロググループ /aws-glue/jobs/output から取得しました。
def run(step, sql, show=True):
start = time.time()
try:
df = spark.sql(sql)
rows = [r.asDict() for r in df.collect()] if show else None
log({"step": step, "sql": sql, "status": "SUCCESS",
"elapsed_sec": round(time.time() - start, 1), "rows": rows})
return True
except Exception as e:
log({"step": step, "sql": sql, "status": "FAILED",
"elapsed_sec": round(time.time() - start, 1), "error": err_info(e)})
return False
Lake Formation の権限を付与する
検証に使ったアカウントは、Lake Formation の CreateDatabaseDefaultPermissions と CreateTableDefaultPermissions が空(新しいデータベースとテーブルに IAMAllowedPrincipals の権限を自動で付けない設定)になっています。このため、IAM ポリシーだけではカタログの操作が許可されませんでした。検証用のリソースに限定して、Lake Formation の権限を付与しました。検証者のロールにも、Athena から MV に書き込むために S3 Tables 側の権限を付けています。
JOB_ROLE=arn:aws:iam::${ACCOUNT_ID}:role/blog-mv-e603a380
ME=arn:aws:iam::${ACCOUNT_ID}:role/cm-user
# ジョブロール: ソースの Glue データベースとテーブル
aws lakeformation grant-permissions --principal DataLakePrincipalIdentifier=$JOB_ROLE \
--resource "{\"Database\":{\"Name\":\"${SRC_DB}\"}}" --permissions ALL
aws lakeformation grant-permissions --principal DataLakePrincipalIdentifier=$JOB_ROLE \
--resource "{\"Table\":{\"DatabaseName\":\"${SRC_DB}\",\"TableWildcard\":{}}}" --permissions ALL
# ジョブロール: S3 Tables のカタログ
aws lakeformation grant-permissions --principal DataLakePrincipalIdentifier=$JOB_ROLE \
--resource "{\"Catalog\":{\"Id\":\"${S3T_CATALOG}\"}}" --permissions DESCRIBE
# ジョブロールと検証者のロール: S3 Tables の namespace とテーブル
for P in $JOB_ROLE $ME; do
aws lakeformation grant-permissions --principal DataLakePrincipalIdentifier=$P \
--resource "{\"Database\":{\"CatalogId\":\"${S3T_CATALOG}\",\"Name\":\"analytics\"}}" --permissions ALL
aws lakeformation grant-permissions --principal DataLakePrincipalIdentifier=$P \
--resource "{\"Table\":{\"CatalogId\":\"${S3T_CATALOG}\",\"DatabaseName\":\"analytics\",\"TableWildcard\":{}}}" --permissions ALL
done
通常 MV とシステム管理型 MV を作成する
ジョブの 1 回目(--phase phase1)では、ソーステーブルを作成して 5 行を投入し、同じ定義で通常 MV とシステム管理型 MV を作成します。違いは MANAGED キーワードの有無だけです。
CREATE TABLE IF NOT EXISTS glue_catalog.blog_mv_src_e603a380.orders (
id INT,
customer_name STRING,
amount DECIMAL(10,2),
order_date DATE
) USING iceberg
TBLPROPERTIES ('format-version'='2');
INSERT INTO glue_catalog.blog_mv_src_e603a380.orders VALUES
(1, 'Alice', 150.00, DATE '2026-10-01'),
(2, 'Bob', 200.50, DATE '2026-10-01'),
(3, 'Alice', 75.25, DATE '2026-10-02'),
(4, 'Carol', 320.00, DATE '2026-10-02'),
(5, 'Bob', 99.99, DATE '2026-10-03');
-- 通常 MV
CREATE MATERIALIZED VIEW s3t_catalog.analytics.customer_summary_regular
AS
SELECT
customer_name,
COUNT(*) AS order_count,
SUM(amount) AS total_amount
FROM glue_catalog.blog_mv_src_e603a380.orders
GROUP BY customer_name;
-- システム管理型 MV
CREATE MANAGED MATERIALIZED VIEW s3t_catalog.analytics.customer_summary_managed
AS
SELECT
customer_name,
COUNT(*) AS order_count,
SUM(amount) AS total_amount
FROM glue_catalog.blog_mv_src_e603a380.orders
GROUP BY customer_name;
aws glue start-job-run --job-name blog-mv-e603a380 --arguments '{"--phase":"phase1"}'
ステートメントごとの結果は次のとおりです。
| ステートメント | 結果 | 所要時間 |
|---|---|---|
| CREATE TABLE(ソース) | SUCCESS | 10.3 秒 |
| INSERT INTO(ソース 5 行) | SUCCESS | 7.0 秒 |
| CREATE MATERIALIZED VIEW(通常) | SUCCESS | 9.6 秒 |
| CREATE MANAGED MATERIALIZED VIEW | SUCCESS | 211.9 秒 |
ソースが 5 行でも、システム管理型 MV の作成には 211.9 秒かかりました。ドキュメントのとおり、CREATE MANAGED MATERIALIZED VIEW 文は AWS Glue が作成を終えるまで戻りませんでした。
SHOW VIEWS では、2 つの MV が FRESH の状態で表示されました。
SHOW VIEWS IN s3t_catalog.analytics;
| namespace | viewName | isTemporary | stalenessStatus |
|---|---|---|---|
| analytics | customer_summary_managed | false | FRESH |
| analytics | customer_summary_regular | false | FRESH |
このセッションは spark.sql.mv.managed.enabled=true で動いていますが、MANAGED を付けずに作成した MV は、次のとおり通常 MV として作成されていました。
2 つの MV の違いを確認する
aws glue get-table に --include-status-details を付けて、2 つの MV を比べます。
% aws glue get-table --catalog-id $S3T_CATALOG --database-name analytics \
--name customer_summary_managed --include-status-details \
--query 'Table.{Name:Name,IsMaterializedView:IsMaterializedView,IsManaged:ViewDefinition.IsManaged,IsProtected:ViewDefinition.IsProtected,Definer:ViewDefinition.Definer,StatusAction:Status.Action,StatusState:Status.State,RequestTime:Status.RequestTime,UpdateTime:Status.UpdateTime}' \
--output json
{
"Name": "customer_summary_managed",
"IsMaterializedView": true,
"IsManaged": true,
"IsProtected": true,
"Definer": "arn:aws:iam::123456789012:role/blog-mv-e603a380",
"StatusAction": "CREATE",
"StatusState": "SUCCESS",
"RequestTime": "2026-10-13T00:50:59+09:00",
"UpdateTime": "2026-10-13T00:54:24+09:00"
}
同じコマンドを通常 MV に実行した結果です。
% aws glue get-table --catalog-id $S3T_CATALOG --database-name analytics \
--name customer_summary_regular --include-status-details \
--query 'Table.{Name:Name,IsMaterializedView:IsMaterializedView,IsManaged:ViewDefinition.IsManaged,IsProtected:ViewDefinition.IsProtected,Definer:ViewDefinition.Definer,StatusAction:Status.Action,StatusState:Status.State,RequestTime:Status.RequestTime,UpdateTime:Status.UpdateTime}' \
--output json
{
"Name": "customer_summary_regular",
"IsMaterializedView": true,
"IsManaged": false,
"IsProtected": true,
"Definer": "arn:aws:iam::123456789012:role/blog-mv-e603a380",
"StatusAction": null,
"StatusState": null,
"RequestTime": null,
"UpdateTime": null
}
システム管理型かどうかは ViewDefinition.IsManaged で判別できます。システム管理型 MV だけに Status が返り、作成要求(00:50:59)から完了(00:54:24)までの時刻が記録されていました。
Spark の DESCRIBE EXTENDED では、列の構成に違いがありました。システム管理型 MV には、定義にない __ivm_s_total_amount と __ivm_num_rec の 2 列が追加されています。
DESCRIBE EXTENDED s3t_catalog.analytics.customer_summary_managed;
col_name data_type comment
customer_name string NULL
order_count bigint NULL
total_amount decimal(20,2) NULL
__ivm_s_total_amount bigint NULL
__ivm_num_rec bigint NULL
# Metadata Columns
_spec_id int
_partition struct<>
_file string
_pos bigint
_deleted boolean
# Detailed Table Information
Name s3t_catalog.analytics.customer_summary_managed
Type MANAGED
Location s3://7a560e21-ea48-467e-eufmujof614fp454ydr7rox8idknkapn1b--table-s3
Provider iceberg
Table_type ICEBERG
Table Properties [createdBy=123456789012,current-snapshot-id=8320873279919889256,format=iceberg/parquet,format-version=2,ownerAccountId=123456789012,s3TableArn=arn:aws:s3tables:ap-northeast-1:123456789012:bucket/blog-mv-e603a380/table/7a560e21-ea48-467e-88ee-5d9475f8e45f,warehouse_location=s3://7a560e21-ea48-467e-eufmujof614fp454ydr7rox8idknkapn1b--table-s3,write.parquet.compression-codec=zstd]
Materialized View Properties [IMV_mvNamespace=analytics,subObjectBaseTableStats=[{"SourceType":"ICEBERG"}],IMV_mvCatalogID=123456789012:s3tablescatalog/blog-mv-e603a380,isMaterializedView=true,IMV_sparkVersion=4.1.1-amzn-0,viewVersionToken=3efb8bcc81583ddfd543,subObjects=[{"Version":"405373681366950278","DatabaseName":"blog_mv_src_e603a380","Region":"ap-northeast-1","CatalogId":"123456789012","Name":"orders"}],viewVersionId=8320873279919889256,tableVersionToken=3efb8bcc81583ddfd543,IMV_ansiEnabled=true,IMV_catalogInfo=[{"catalogId":"123456789012","catalogName":"glue_catalog"}],stalenessStatus=FRESH,IMV_region=ap-northeast-1,lastRefreshType=FULL,isManaged=true,viewOriginalText=SELECT
customer_name,
COUNT(*) AS order_count,
SUM(amount) AS total_amount
FROM glue_catalog.blog_mv_src_e603a380.orders
GROUP BY customer_name]
Statistics 1741 bytes, 3 rows NULL
通常 MV の列は customer_name、order_count、total_amount の 3 列だけで、Materialized View Properties は isManaged=false でした。なお、Type の MANAGED は通常 MV でも同じ値で、システム管理型かどうかは isManaged で判別します。ドキュメントには、__ivm で始まる列はシステムが使うために予約されていると書かれています。
Spark から MV に書き込んでみる
ジョブのロールは S3 Tables バケットに s3tables:* を許可されています。このロールで、2 つの MV に同じ書き込みを試しました。
-- 2 つの MV に実行
INSERT INTO s3t_catalog.analytics.customer_summary_regular VALUES ('Mallory_sql', 999, 99999.99);
INSERT INTO s3t_catalog.analytics.customer_summary_managed VALUES ('Mallory_sql', 999, 99999.99, 1, 1);
-- DataFrame API でも 1 行 append(writeTo(...).append())
-- 削除
DELETE FROM s3t_catalog.analytics.customer_summary_regular WHERE customer_name = 'Mallory_df';
DELETE FROM s3t_catalog.analytics.customer_summary_managed WHERE customer_name = 'Alice';
-- システム管理型 MV のみ: 列の追加
ALTER TABLE s3t_catalog.analytics.customer_summary_managed ADD COLUMNS (note STRING);
最初はシステム管理型 MV にも 3 列で INSERT を実行したところ、__ivm の 2 列の分だけ列数が足りず、INSERT_COLUMN_ARITY_MISMATCH.NOT_ENOUGH_DATA_COLUMNS で失敗しました。これは書き込み保護による拒否ではないため、5 列にそろえて再実行しています。
結果は次のとおりです。
| 操作 | 通常 MV | システム管理型 MV |
|---|---|---|
INSERT INTO |
SUCCESS | FAILED |
DataFrame writeTo(...).append() |
SUCCESS | FAILED |
DELETE FROM |
SUCCESS | FAILED |
ALTER TABLE ADD COLUMNS |
実施せず | FAILED |
システム管理型 MV への INSERT、append、DELETE は、いずれも Spark のタスクがデータファイルを書き込む段階で失敗しました。エラーログの原因(Caused by)は S3 の AccessDenied(403)です。
INSERT のときに Glue のエラーログ(/aws-glue/jobs/error)にドライバーが出力したログの全文です。タスクは 4 回失敗し、最後の試行(TID 3)のスタックトレースが記録されています。
ALTER TABLE ADD COLUMNS は、Spark の段階で拒否されました。マテリアライズドビューは、変更ができないからです。
UnsupportedOperationException: Cannot apply unsupported change to managed materialized view: org.apache.spark.sql.connector.catalog.TableChange$AddColumn@4181d81
書き込み後の通常 MV には、定義クエリの結果にない行が入りました(この後 DELETE で Mallory_df の行を消しています)。
SELECT * FROM s3t_catalog.analytics.customer_summary_regular ORDER BY customer_name;
| customer_name | order_count | total_amount |
|---|---|---|
| Alice | 2 | 225.25 |
| Bob | 2 | 300.49 |
| Carol | 1 | 320.00 |
| Mallory_df | 1 | 88888.88 |
| Mallory_sql | 999 | 99999.99 |
Iceberg のスナップショットも、通常 MV では書き込みのたびに増えました。システム管理型 MV のスナップショットは、作成時の 1 件だけのままです(committed_at は UTC)。
SELECT committed_at, snapshot_id, operation
FROM s3t_catalog.analytics.customer_summary_regular.snapshots ORDER BY committed_at;
| committed_at | snapshot_id | operation |
|---|---|---|
| 2026-10-12 15:50:57.455 | 8870572473500367875 | overwrite |
| 2026-10-12 15:54:37.040 | 7556935525565095869 | append |
| 2026-10-12 15:54:41.483 | 7176343283781109168 | append |
| 2026-10-12 15:57:45.607 | 3453113367357939935 | delete |
SELECT committed_at, snapshot_id, operation
FROM s3t_catalog.analytics.customer_summary_managed.snapshots ORDER BY committed_at;
| committed_at | snapshot_id | operation |
|---|---|---|
| 2026-10-12 15:54:07.033 | 8320873279919889256 | overwrite |
Athena から MV を読み書きしてみる
次は、検証者のロールで Athena から操作します。まず 2 つの MV を読み取ります。
% aws athena start-query-execution \
--work-group primary \
--query-execution-context Catalog=s3tablescatalog/${TABLE_BUCKET},Database=analytics \
--result-configuration OutputLocation=s3://${BUCKET}/athena-results/ \
--query-string "SELECT * FROM customer_summary_managed ORDER BY customer_name"
| customer_name | order_count | total_amount | __ivm_s_total_amount | __ivm_num_rec |
|---|---|---|---|---|
| Alice | 2 | 225.25 | 2 | 2 |
| Bob | 2 | 300.49 | 2 | 2 |
| Carol | 1 | 320.00 | 1 | 1 |
システム管理型 MV も Athena から読み取れました。SELECT * では __ivm の 2 列も返ります。通常 MV も読み取れ、Spark で書き込んだ Mallory_sql の行が含まれていました。
続いて、2 つの MV に INSERT を実行します。
% aws athena start-query-execution \
--work-group primary \
--query-execution-context Catalog=s3tablescatalog/${TABLE_BUCKET},Database=analytics \
--result-configuration OutputLocation=s3://${BUCKET}/athena-results/ \
--query-string "INSERT INTO customer_summary_managed VALUES ('Mallory_athena', 1, 77777.77, 1, 1)"
% aws athena get-query-execution --query-execution-id b518c362-b82b-4574-834b-654fc84c4ed8 \
--query 'QueryExecution.Status.{State:State,Reason:StateChangeReason,ErrorCategory:AthenaError.ErrorCategory,ErrorType:AthenaError.ErrorType}'
通常 MV への INSERT INTO customer_summary_regular VALUES ('Mallory_athena', 1, 77777.77) は SUCCEEDED になりました。システム管理型 MV への INSERT は、S3 の 403 で失敗しました。
{
"State": "FAILED",
"Reason": "PERMISSION_DENIED: Access Denied (Service: S3, Status Code: 403, Request ID: TMS75TKSBSYR1HS9, Extended Request ID: Knkn42T+WVzFwW1tRifvN7yxUzFE19U+7fA/5cgWQPARx/8aL75/ldxSzuKmQFStWfFewe7yios=). If a data manifest file was generated at 's3://blog-mv-e603a380/athena-results/b518c362-b82b-4574-834b-654fc84c4ed8-manifest.csv', you may need to manually clean the data from locations specified in the manifest. Athena will not delete data in your account.",
"ErrorCategory": 2,
"ErrorType": 1500
}
AWS Glue の UpdateTable API で定義を変更してみる
カタログ経由の変更として、検証者のロールで aws glue update-table を直接呼び出しました。get-table の結果を基に TableInput を作り、ViewDefinition.IsManaged を false にして保護を外せるかを試します。
S3 Tables のカタログにある MV に update-table を実行するには、次の 3 点が必要でした。いずれも、試行中に返ったエラーで判明したものです。
--version-idを指定する(指定しないと S3 Tables 側でversionTokenが null というエラーになる)ViewDefinitionを含める場合は--view-update-actionを指定するTableTypeをEXTERNAL_TABLEにする(get-tableはcustomerを返すが、そのまま渡すとMaterialized views must have table type EXTERNAL_TABLE.になる)
% aws glue get-table --catalog-id $S3T_CATALOG --database-name analytics \
--name customer_summary_managed --output json > current_managed.json
% jq '.Table | {Name, Description: "tampered by UpdateTable", TableType: "EXTERNAL_TABLE", Parameters, StorageDescriptor,
ViewDefinition: (.ViewDefinition | {IsProtected, Definer, ViewVersionId, ViewVersionToken,
SubObjects, SubObjectVersionIds, LastRefreshType,
Representations: [.Representations[] | {Dialect, DialectVersion, ViewOriginalText}],
IsManaged: false})}' current_managed.json > table_input.json
% aws glue update-table --catalog-id $S3T_CATALOG --database-name analytics \
--version-id $(jq -r '.Table.VersionId' current_managed.json) \
--view-update-action REPLACE \
--table-input file://table_input.json
An error occurred (InvalidInputException) when calling the UpdateTable operation: Cannot modify ViewDefinition.IsManaged on a system-managed materialized view. This field is immutable after creation.
IsManaged は作成後に変更できない項目として拒否されました。ViewDefinition を含めずに TableInput を渡した場合も、システム管理型 MV では拒否されました。
An error occurred (InvalidInputException) when calling the UpdateTable operation: Cannot modify ViewDefinition on a system-managed materialized view. This field is immutable after creation.
同じく ViewDefinition を含めない update-table は、通常 MV では成功を返しました。ただし、直後の get-table では ViewDefinition が残っていて、MV の定義は消えていませんでした。
定義の SQL(ViewOriginalText)を SUM(amount) * 2 に書き換える更新は、通常 MV でもシステム管理型 MV でも、同じエラーで拒否されました。SQL 定義の書き換えは MV 全般で受け付けられず、この点に 2 つの MV の差はありませんでした。
An error occurred (InvalidInputException) when calling the UpdateTable operation: Update of ViewOriginalText for Materialized View is not available.
なお、ViewDefinition を変えずに Description だけを変える更新は、2 つの MV とも成功を返しました。ただし、どちらも get-table の Description は null のままでした。
S3 Tables の API で Iceberg のメタデータを差し替えてみる
基盤の Iceberg テーブルへの直接の書き込みとして、S3 Tables の UpdateTableMetadataLocation API で、テーブルが参照する Iceberg のメタデータファイルを差し替えます。通常 MV は、Athena で INSERT する前のメタデータファイル(00004-5734b581-e4f9-4ae7-832c-b8939b15f275.metadata.json)に戻します。
% aws s3tables get-table-metadata-location --table-bucket-arn $TB_ARN \
--namespace analytics --name customer_summary_regular
{
"versionToken": "1a99953d59198d0ce3e4",
"metadataLocation": "s3://f1b961f4-c1f9-4ffd-mec3po58st48mi4a5t3a4bjea9gfhapn1b--table-s3/metadata/00005-98a09a9f-9073-4150-8b85-d4501244a6bb.metadata.json",
"warehouseLocation": "s3://f1b961f4-c1f9-4ffd-mec3po58st48mi4a5t3a4bjea9gfhapn1b--table-s3"
}
% aws s3tables update-table-metadata-location --table-bucket-arn $TB_ARN \
--namespace analytics --name customer_summary_regular \
--version-token 1a99953d59198d0ce3e4 \
--metadata-location s3://f1b961f4-c1f9-4ffd-mec3po58st48mi4a5t3a4bjea9gfhapn1b--table-s3/metadata/00004-5734b581-e4f9-4ae7-832c-b8939b15f275.metadata.json
{
"name": "customer_summary_regular",
"tableARN": "arn:aws:s3tables:ap-northeast-1:123456789012:bucket/blog-mv-e603a380/table/f1b961f4-c1f9-4ffd-bd45-5c5ff2a62d62",
"namespace": [
"analytics"
],
"versionToken": "cf43e6e34e76a2516aa1",
"metadataLocation": "s3://f1b961f4-c1f9-4ffd-mec3po58st48mi4a5t3a4bjea9gfhapn1b--table-s3/metadata/00004-5734b581-e4f9-4ae7-832c-b8939b15f275.metadata.json"
}
通常 MV では差し替えが成功しました。システム管理型 MV のメタデータファイルは最初のファイル(00000-db037a92-7d84-405d-a549-b333c14fe896.metadata.json)のままで戻す先がないため、中身を変えない指定でも API の呼び出し自体が拒否されるかを確認します。システム管理型 MV に、現在のメタデータファイルと versionToken をそのまま指定して同じ API を呼び出すと、拒否されました。
% aws s3tables update-table-metadata-location --table-bucket-arn $TB_ARN \
--namespace analytics --name customer_summary_managed \
--version-token 3efb8bcc81583ddfd543 \
--metadata-location s3://7a560e21-ea48-467e-eufmujof614fp454ydr7rox8idknkapn1b--table-s3/metadata/00000-db037a92-7d84-405d-a549-b333c14fe896.metadata.json
An error occurred (AccessDeniedException) when calling the UpdateTableMetadataLocation operation: User: arn:aws:sts::123456789012:assumed-role/cm-user/ishikawa is not authorized to perform: s3tables:UpdateTableMetadataLocation on resource: arn:aws:s3tables:ap-northeast-1:123456789012:bucket/blog-mv-e603a380/table/7a560e21-ea48-467e-88ee-5d9475f8e45f with an explicit deny in a resource-based policy
エラーメッセージは、リソースベースポリシーの明示的な Deny による拒否を示しています。一方、get-table-policy と get-table-bucket-policy では、2 つのテーブルにもテーブルバケットにもポリシーは存在しませんでした。
% aws s3tables get-table-policy --table-bucket-arn $TB_ARN \
--namespace analytics --name customer_summary_managed
An error occurred (NotFoundException) when calling the GetTablePolicy operation: The specified table policy does not exist.
メタデータを差し替えた後、通常 MV を Athena から読み取ります。
% aws athena start-query-execution \
--work-group primary \
--query-execution-context Catalog=s3tablescatalog/${TABLE_BUCKET},Database=analytics \
--result-configuration OutputLocation=s3://${BUCKET}/athena-results/ \
--query-string "SELECT * FROM customer_summary_regular ORDER BY customer_name"
クエリは失敗し、通常 MV は Athena から読み取れなくなっていました。
NOT_SUPPORTED: Materialized view integrity check failed for (catalog:123456789012:s3tablescatalog/blog-mv-e603a380$schema:analytics, customer_summary_regular): versionId (cf43e6e34e76a2516aa1) != viewVersionToken (1a99953d59198d0ce3e4)
S3 Tables 側のバージョン(cf43e6e34e76a2516aa1)と、Glue Data Catalog の MV 定義に記録されたバージョン(1a99953d59198d0ce3e4)が一致しないというエラーです。
続いて、Glue ジョブの Spark から通常 MV を読み取り、エラーメッセージが案内している REFRESH を実行しました。
SELECT * FROM s3t_catalog.analytics.customer_summary_regular ORDER BY customer_name;
REFRESH MATERIALIZED VIEW s3t_catalog.analytics.customer_summary_regular FULL;
どちらも、同じ整合性チェックの ValidationException で失敗しました。
org.apache.iceberg.exceptions.ValidationException: Materialized view customer_summary_regular integrity check failed: versionId (cf43e6e34e76a2516aa1) != viewVersionToken (1a99953d59198d0ce3e4). Please recreate the materialized view or run a full refresh
ただし、失敗した REFRESH の後に同じジョブで実行した SELECT * は成功し、次の 4 行が返りました。
| customer_name | order_count | total_amount |
|---|---|---|
| Alice | 2 | 225.25 |
| Bob | 2 | 300.49 |
| Carol | 1 | 320.00 |
| Mallory_sql | 999 | 99999.99 |
差し替え先のメタデータファイルの内容(Athena で INSERT する前に読み取った内容)と同じで、Mallory_athena の行はなく、Mallory_sql の行は残っていました。定義クエリで計算し直した結果ではありません。直後に実行した snapshots メタデータテーブル(customer_summary_regular.snapshots)の読み取りは、再び同じ ValidationException で失敗しました。この SELECT * だけが成功した理由は確認できていません。
許可される操作を試す
ドキュメントで許可されているシステム管理型 MV の操作を試しました。
REFRESH
ソーステーブルに 2 行を追加すると、システム管理型 MV の stalenessStatus は STALE になりました。
INSERT INTO glue_catalog.blog_mv_src_e603a380.orders VALUES
(6, 'Carol', 180.00, DATE '2026-10-04'),
(7, 'Dave', 50.00, DATE '2026-10-04');
REFRESH MATERIALIZED VIEW s3t_catalog.analytics.customer_summary_managed FULL;
最初の REFRESH は、IAM の権限不足で失敗しました。ここでも、ジョブスクリプトが記録したのはエラーメッセージの先頭 1,500 文字だけなので、スタックトレースより前の部分を載せます。
An error occurred while calling o147.sql.
: org.apache.iceberg.shaded.software.amazon.awssdk.services.glue.model.AccessDeniedException: User: arn:aws:sts::123456789012:assumed-role/blog-mv-e603a380/GlueJobRunnerSession is not authorized to perform: glue:StartMaterializedViewRefreshTaskRun because no identity-based policy allows the glue:StartMaterializedViewRefreshTaskRun action (Service: Glue, Status Code: 400, Request ID: e072e336-6e4c-48c0-96dc-061df4f29da5) (SDK Attempt Count: 1)
glue:StartMaterializedViewRefreshTaskRun は、デベロッパーガイドの IAM ポリシー例には含まれていない権限です。Glue の Materialized view API には更新タスクの開始、停止、取得、一覧の 4 つの操作があるため、4 つともジョブロールに追加しました。
{
"Sid": "MVRefreshTaskRun",
"Effect": "Allow",
"Action": [
"glue:StartMaterializedViewRefreshTaskRun",
"glue:StopMaterializedViewRefreshTaskRun",
"glue:GetMaterializedViewRefreshTaskRun",
"glue:ListMaterializedViewRefreshTaskRuns"
],
"Resource": "*"
}
権限を追加して再実行すると、REFRESH は成功しました。所要時間は 456.5 秒です。
SELECT * FROM s3t_catalog.analytics.customer_summary_managed ORDER BY customer_name;
| customer_name | order_count | total_amount | __ivm_s_total_amount | __ivm_num_rec |
|---|---|---|---|---|
| Alice | 2 | 225.25 | 2 | 2 |
| Bob | 2 | 300.49 | 2 | 2 |
| Carol | 2 | 500.00 | 2 | 2 |
| Dave | 1 | 50.00 | 1 | 1 |
追加した Carol と Dave の注文が反映され、スナップショットが 1 件(overwrite)増えました。更新タスクの記録は list-materialized-view-refresh-task-runs で確認できます。
% aws glue list-materialized-view-refresh-task-runs --catalog-id $S3T_CATALOG \
--database-name analytics --table-name customer_summary_managed
{
"MaterializedViewRefreshTaskRuns": [
{
"CustomerId": "123456789012",
"MaterializedViewRefreshTaskRunId": "b604f9ff-8373-421e-92cb-9f951246e0b6",
"DatabaseName": "analytics",
"TableName": "customer_summary_managed",
"CatalogId": "123456789012:s3tablescatalog/blog-mv-e603a380",
"Role": "arn:aws:iam::123456789012:role/blog-mv-e603a380",
"Status": "SUCCEEDED",
"CreationTime": "2026-10-13T01:06:35+09:00",
"LastUpdated": "2026-10-13T01:10:19+09:00",
"EndTime": "2026-10-13T01:10:18+09:00",
"DPUSeconds": 240.3
}
]
}
更新タスクは 01:06:35 に作成されて 01:10:18 に終わり、240.3 DPU 秒が記録されていました。Spark の REFRESH 文は 01:06:29 に始まり、01:14:06 に戻っています。Athena からも、更新後の 4 行を読み取れました。
テーブルプロパティの変更
ALTER TABLE ... SET TBLPROPERTIES は成功しました。スナップショットは変わっていません。
ALTER TABLE s3t_catalog.analytics.customer_summary_managed SET TBLPROPERTIES ('blog.note'='updated-by-owner');
SHOW TBLPROPERTIES s3t_catalog.analytics.customer_summary_managed;
| key | value |
|---|---|
| blog.note | updated-by-owner |
| createdBy | 123456789012 |
| current-snapshot-id | 8320873279919889256 |
| format | iceberg/parquet |
| format-version | 2 |
| ownerAccountId | 123456789012 |
| s3TableArn | arn:aws:s3tables:ap-northeast-1:123456789012:bucket/blog-mv-e603a380/table/7a560e21-ea48-467e-88ee-5d9475f8e45f |
| warehouse_location | s3://7a560e21-ea48-467e-eufmujof614fp454ydr7rox8idknkapn1b--table-s3 |
| write.parquet.compression-codec | zstd |
更新スケジュールの変更
ドキュメントには、AWS Glue 6.0 以降は自動更新の最小間隔が 10 分と書かれています。そこで EVERY 10 MINUTES を指定しましたが、構文エラーになりました。単数形の EVERY 10 MINUTE も同じ構文エラーでした。
[PARSE_SYNTAX_ERROR] Syntax error at or near 'MINUTES'. SQLSTATE: 42601 (line 1, pos 101)
== SQL ==
ALTER MATERIALIZED VIEW s3t_catalog.analytics.customer_summary_managed ADD SCHEDULE REFRESH EVERY 10 MINUTES
-----------------------------------------------------------------------------------------------------^^^
ドキュメントの例と同じ時間単位で指定すると成功し、DESCRIBE EXTENDED の Materialized View Properties に schedule=3600 が追加されました。
ALTER MATERIALIZED VIEW s3t_catalog.analytics.customer_summary_managed ADD SCHEDULE REFRESH EVERY 1 HOUR;
ALTER MATERIALIZED VIEW s3t_catalog.analytics.customer_summary_managed DROP SCHEDULE;
続けて実行した DROP SCHEDULE も成功を返しました。しかし、直後の get-table では RefreshSeconds が 3600 のままでした。
% aws glue get-table --catalog-id $S3T_CATALOG --database-name analytics \
--name customer_summary_managed \
--query 'Table.{IsManaged:ViewDefinition.IsManaged,RefreshSeconds:ViewDefinition.RefreshSeconds,LastRefreshType:ViewDefinition.LastRefreshType}'
{
"IsManaged": true,
"RefreshSeconds": 3600,
"LastRefreshType": "FULL"
}
さらに、DROP SCHEDULE を実行した 01:14:21 から約 1 分後の 01:15:22 に、更新タスクが 1 件開始されていました。手動では起動していないタスクです。stop-materialized-view-refresh-task-run で停止し、STOPPED(0.0 DPU 秒)になったことを確認しました。
% aws glue stop-materialized-view-refresh-task-run --catalog-id $S3T_CATALOG \
--database-name analytics --table-name customer_summary_managed
--------------------------------------------------------------------------------------------------------------------------
| ListMaterializedViewRefreshTaskRuns |
+---------------------------+--------+----------------------------+----------------------------------------+-------------+
| Created | DPU | End | Id | Status |
+---------------------------+--------+----------------------------+----------------------------------------+-------------+
| 2026-10-13T01:15:22+09:00| 0.0 | 2026-10-13T01:16:04+09:00 | 76566af4-49a0-4767-9590-072d6610a86d | STOPPED |
| 2026-10-13T01:06:35+09:00| 240.3 | 2026-10-13T01:10:18+09:00 | b604f9ff-8373-421e-92cb-9f951246e0b6 | SUCCEEDED |
+---------------------------+--------+----------------------------+----------------------------------------+-------------+
考察
書き込みが拒否された場所
システム管理型 MV への書き込みは、3 つの段階で拒否されました。
- Spark:
ALTER TABLE ADD COLUMNSは、Spark がCannot apply unsupported change to managed materialized viewを返した - AWS Glue API:
UpdateTableでViewDefinitionやIsManagedを変えようとすると、This field is immutable after creation.が返った - ストレージ: Spark と Athena のデータ書き込みは S3 の 403 で失敗し、S3 Tables の
UpdateTableMetadataLocationは「リソースベースポリシーの明示的な Deny」で拒否された。この Deny は、GetTablePolicyで確認できるテーブルポリシーには入っていなかった
拒否された操作を実行したのは、テーブルバケットに s3tables:* を許可したジョブロールと、検証者のロールです。どちらのロールも、データの書き込み(Spark と Athena の INSERT など)とメタデータファイルの差し替えは、通常 MV に対しては成功しています。What's New にある「テーブルやその基盤となる S3 ファイルへの書き込みアクセス権を誰が持っていても、計算結果を変更されることなく維持できる」という説明と一致する結果です。
通常 MV で起きたこと
通常 MV では、Spark と Athena からの INSERT や DELETE がエラーなく完了し、定義クエリの結果にない行(order_count が 999 の Mallory_sql など)がそのまま読み取れる状態になりました。
S3 Tables の API でメタデータファイルを差し替えた場合は、Glue Data Catalog に記録されたバージョンと一致しなくなり、Athena と Spark の両方で読み取りが失敗しました。REFRESH ... FULL も同じチェックで失敗したため、今回の検証では、REFRESH でこの通常 MV を読み取れる状態に戻すことはできませんでした。失敗した REFRESH の後、Spark の SELECT * が 1 回だけ差し替え先のメタデータファイルの内容を返しましたが、直後の読み取りは再び失敗しています。
時間と DPU の消費
ソースは 7 行だけでしたが、システム管理型 MV の処理には次の時間がかかりました。
CREATE MANAGED MATERIALIZED VIEW: 211.9 秒(通常 MV のCREATEは 9.6 秒)REFRESH ... FULL: 456.5 秒(更新タスク自体は 01:06:35 から 01:10:18、240.3 DPU 秒)
Spark の文は、AWS Glue の処理が終わるまで戻りません。その間も Glue ジョブのワーカーは確保されたままです。今回の 6 回のジョブ実行の合計は 2,089 DPU 秒で、そのうち REFRESH を含む 1 回が 1,021 DPU 秒でした。Spark ジョブから作成や REFRESH を実行する場合は、待ち時間の分もジョブの DPU として消費される点を考慮する必要があります。なお、自動更新の料金は AWS ブログで 1 DPU 時間あたり 0.44 USD(1 秒単位、1 分の最小課金)と説明されています。
注意点
- システム管理型 MV には、AWS Glue 6.0 と S3 Tables が必要。S3 汎用バケットの MV は対象外
__ivm_s_total_amountのような__ivm列が追加され、Athena のSELECT *でも返る。共有先の利用者には、必要な列を指定して読み取るように案内したほうがよい- REFRESH には
glue:StartMaterializedViewRefreshTaskRunが必要で、ドキュメントの IAM ポリシー例には含まれていない - Lake Formation で新規リソースへの既定の権限を無効にしているアカウントでは、IAM ポリシーに加えて Lake Formation の権限付与が必要
- 今回の検証では、分単位のスケジュール指定(
MINUTES/MINUTE)は構文エラーになり、時間単位の指定だけが通った DROP SCHEDULEが成功を返しても、get-tableのRefreshSecondsが残っていることがあった。スケジュールを外した後は、RefreshSecondsとlist-materialized-view-refresh-task-runsで確認したほうがよい- スキーマ進化は非対応のため、定義を変えるときは削除して作り直す
今後に期待すること
- 分単位の更新間隔の指定方法がドキュメントに記載されること
- デベロッパーガイドの IAM ポリシー例に、MV 更新タスクの API が追加されること
DROP SCHEDULE実行後にスケジュールが残った事象について、挙動の説明または修正
最後に
AWS Glue のシステム管理型 MV を、通常 MV と並べて試しました。システム管理型 MV では、Spark と Athena からのデータの書き込み、UpdateTable による定義の変更、S3 Tables の API によるメタデータの差し替えが、書き込み権限を持つロールからでもすべて拒否されました。一方で、Athena からの読み取り、REFRESH、テーブルプロパティの変更、削除は行えました。
集計結果を MV として他のチームに提供していて、利用者側の書き込み権限の設定に関係なく結果を変更されないようにしたい場合は、システム管理型 MV が選択肢になります。採用する場合は、AWS Glue 6.0 と S3 Tables を前提に、__ivm 列の扱い、REFRESH に必要な IAM 権限、作成と更新にかかる時間を事前に確認してください。










