[アップデート] AWS Glue zero-ETL integration で同じテーブルへの二重書き込みが ConflictException で止まるようになったので試してみた
クラウド事業統括本部の石川です。AWS Glue の zero-ETL integration で、ターゲットテーブルのプロパティに所有権が記録されるようになり、2つの integration が同じテーブルへ書き込む構成を作れなくなりました。実際に競合を起こして確かめてみました。
AWS Glue の zero-ETL integration は、DynamoDB や SaaS アプリケーションのデータを、ETLジョブを書かずに Amazon S3 Tables や SageMaker Lakehouse のカタログへ複製する機能です。
zero-ETL integration が書き込むターゲットテーブルのプロパティに「どの integration が所有しているか」が記録されるようになりました。そして、既に別の integration が所有している組み合わせに対して新しい integration を作ろうとすると、AWS Glue が競合を検出して作成を拒否します。
複数の zero-ETL integration を運用していると、同じソーステーブルを同じターゲットへ向けた integration をうっかり二重に作ってしまい、片方がもう片方のデータを上書きする事故が起こり得ます。今回のアップデートは、この事故を作成時点で止めるものです。
ターゲットテーブルプロパティの所有権とは
公式ドキュメントによると、ターゲットテーブルプロパティを識別するのは「ターゲットリソースのARN」と「ソーステーブル名」 の2つです。ターゲットリソースとは、汎用S3バケットをターゲットにする場合は AWS Glue データベース、Amazon S3 Tables をターゲットにする場合は S3 Tables カタログを指します。
この組み合わせ1つにつき、紐づけられる integration は同時に1つだけです。結果として、2つの integration が同じソーステーブルを同じターゲットリソースへ書き込むことはできません。この制約はマネジメントコンソール、AWS CLI、APIのいずれから操作しても適用されます。
やってみた
前提条件
- 検証リージョン: ap-northeast-1
- AWS CLI: 2.36.45(執筆時点の最新バージョンにアップデート)
- ソース: Amazon DynamoDB テーブル(単一テーブル)
- ターゲット: 汎用S3バケットを紐づけた AWS Glue データベース
今回はソースを DynamoDB にしました。ターゲットは、今回のアップデートで名指しされた Amazon S3 Tables ではなく、汎用S3バケット + Glue データベースの構成を選んでいます。所有権の識別単位はどちらも「ターゲットリソース × ソーステーブル名」で共通のため、前提リソースが少ない後者で検証しました。
検証環境
ソース側
ソース側は、DynamoDB テーブル zetl_orders を作成し、サンプルデータを3件投入しました。zero-ETL のソースにするには Point-in-time recovery(PITR)の有効化が必要です。あわせて、AWS Glue がテーブルをエクスポートできるよう、リソースベースポリシーで glue.amazonaws.com に dynamodb:ExportTableToPointInTime と dynamodb:DescribeTable、dynamodb:DescribeExport を許可します。
ターゲット側
ターゲット側は、S3 バケットを作り、そのパスを LocationUri に指定した Glue データベース zetl_blog を作成しました。LocationUri の指定は zero-ETL の必須要件です。続いて、AWS Glue がこのデータベースへ書き込むための IAM ロールを用意し、create-integration-resource-property でターゲットリソースに関連付けます。最後に、Glue データカタログのリソースポリシーへ glue:CreateInboundIntegration と glue:AuthorizeInboundIntegration を追加します。前者は integration を作る側に、後者は glue.amazonaws.com に許可するもので、両方ないと integration の作成が通りません。
ターゲットテーブルプロパティを作成する
ターゲットテーブルプロパティを作成します。出力されるテーブル名を orders_i1 に指定しました。
% aws glue create-integration-table-properties \
--resource-arn arn:aws:glue:ap-northeast-1:123456789012:database/zetl_blog \
--table-name zetl_orders \
--target-table-config '{"UnnestSpec":"FULL","TargetTableName":"orders_i1"}'
ここで、今回追加された list-integration-table-properties で状態を見てみます。--filters でターゲットを絞り込めます。
% aws glue list-integration-table-properties \
--filters 'Name=TargetArn,Values=arn:aws:glue:ap-northeast-1:123456789012:database/zetl_blog'
{
"IntegrationTablePropertiesList": [
{
"ResourceArn": "arn:aws:glue:ap-northeast-1:123456789012:database/zetl_blog",
"TableName": "zetl_orders",
"TargetTableConfig": {
"UnnestSpec": "FULL",
"PartitionSpec": [],
"TargetTableName": "orders_i1"
}
}
]
}
この時点では IntegrationArn が返っていません。テーブルプロパティは作られたものの、まだ所有者がいない状態です。
integration を作成すると所有権が記録される
zero-ETL integration を作成します。
% aws glue create-integration \
--integration-name zetl-blog-i1 \
--source-arn arn:aws:dynamodb:ap-northeast-1:123456789012:table/zetl_orders \
--target-arn arn:aws:glue:ap-northeast-1:123456789012:database/zetl_blog
{
"SourceArn": "arn:aws:dynamodb:ap-northeast-1:123456789012:table/zetl_orders",
"TargetArn": "arn:aws:glue:ap-northeast-1:123456789012:database/zetl_blog",
"IntegrationName": "zetl-blog-i1",
"IntegrationArn": "arn:aws:glue:ap-northeast-1:123456789012:integration:be1697bf-04db-4fea-9de2-6c65f873f4aa",
"Status": "CREATING",
"CreateTime": "2026-09-15T19:54:37.291000+09:00",
"DataFilter": "include: *"
}
Status が CREATING のうちに、もう一度テーブルプロパティを見てみます。
% aws glue list-integration-table-properties \
--filters 'Name=TargetArn,Values=arn:aws:glue:ap-northeast-1:123456789012:database/zetl_blog'
{
"IntegrationTablePropertiesList": [
{
"ResourceArn": "arn:aws:glue:ap-northeast-1:123456789012:database/zetl_blog",
"TableName": "zetl_orders",
"TargetTableConfig": {
"UnnestSpec": "FULL",
"PartitionSpec": [],
"TargetTableName": "orders_i1",
"IntegrationArn": "arn:aws:glue:ap-northeast-1:123456789012:integration:be1697bf-04db-4fea-9de2-6c65f873f4aa"
}
}
]
}
**IntegrationArn が追加され、作成した integration のARNが入りました。**所有権は integration が ACTIVE になるのを待たず、CREATING の段階で記録されています。
同じ組み合わせで2つ目を作ると競合する
同じ DynamoDB テーブルを、同じ Glue データベースへ向けた2つ目の integration を作ってみます。
% aws glue create-integration \
--integration-name zetl-blog-i2 \
--source-arn arn:aws:dynamodb:ap-northeast-1:123456789012:table/zetl_orders \
--target-arn arn:aws:glue:ap-northeast-1:123456789012:database/zetl_blog
An error occurred (ConflictException) when calling the CreateIntegration operation:
Source table 'zetl_orders' is already being written to the same target by integration(s)
arn:aws:glue:ap-northeast-1:123456789012:integration:be1697bf-04db-4fea-9de2-6c65f873f4aa.
Please choose a different target ARN or check the existing integration(s).
**ConflictException で拒否されました。**注目したいところは、エラーメッセージに所有している integration のARNがそのまま含まれている点です。「誰かが既に書いている」で終わらず、どの integration が持っているかまで返ってきます。競合に遭遇した担当者は、このARNを describe-integrations に渡せば、相手の integration の名前や作成日時を特定できます。
% aws glue describe-integrations \
--integration-identifier arn:aws:glue:ap-northeast-1:123456789012:integration:be1697bf-04db-4fea-9de2-6c65f873f4aa \
--query 'Integrations[].{Name:IntegrationName,Status:Status,Created:CreateTime}' \
--output table
-------------------------------------------------------------------
| DescribeIntegrations |
+---------------+----------+--------------------------------------+
| Name | Status | Created |
+---------------+----------+--------------------------------------+
| zetl-blog-i1 | ACTIVE | 2026-09-15T19:54:37.268000+09:00 |
+---------------+----------+--------------------------------------+
なお --integration-identifier には integration 名ではなくARNを渡す必要があります。名前を渡すと ValidationException が返りました。
考察
今回の検証で確認できた点を整理します。
-
ターゲットテーブルプロパティを作った直後は
IntegrationArnが付かず、integration を作成すると付きました。テーブルプロパティの作成と所有権の記録は、別のタイミングで起こります。 -
所有権が記録されるのは integration が
CREATINGになった時点です。ACTIVEになるのを待ちません。create-integrationが応答を返した直後には、そのテーブルプロパティは既に所有された状態でした。 -
同じソーステーブルを同じターゲットリソースへ向けた2つ目の integration は
ConflictExceptionで拒否されます。エラーメッセージに所有している integration のARNが含まれる点は、運用上の効果があります。複数チームが同じアカウントで zero-ETL を運用している場合、競合の相手をエラーメッセージだけで特定できます。
公式ドキュメントに記載によると、制約の単位は「ターゲットリソース × ソーステーブル名」です。同じソーステーブルでも、ターゲットリソースが異なれば別の組み合わせとして扱われます。逆に言えば、1つの Glue データベースへ同じソーステーブルを流し込む経路は常に1本に限られます。また、同時変更の検出だけは他の条件と扱いが異なり、作成をブロックしません。別のユーザーが同じ出力設定を更新した場合、最新を取得し直すか、そのまま進めるかを選べます。
最後に
AWS Glue zero-ETL integration のターゲットテーブルプロパティに所有権が記録されるようになり、同じソーステーブルを同じターゲットリソースへ書き込む integration を二重に作れなくなりました。競合は ConflictException として作成時に返り、メッセージには所有している integration のARNが含まれます。
同時に追加された ListIntegrationTableProperties によって、アカウント内のターゲットテーブルプロパティを一覧できるようになりました。今回の検証でも、所有権が記録される前と後の差分をこのAPIで確認しています。複数の zero-ETL integration を運用している環境では、どのテーブルプロパティをどの integration が持っているかを、この一覧で把握できます。
利用には AWS CLI 2.36.45 以降、または botocore 1.43.94 以降が必要です。それより古いバージョンでは IntegrationArn フィールドも一覧APIも扱えません。







