[アップデート] AWS Glue zero-ETL integration で同じテーブルへの二重書き込みが ConflictException で止まるようになったので試してみた

[アップデート] AWS Glue zero-ETL integration で同じテーブルへの二重書き込みが ConflictException で止まるようになったので試してみた

AWS Glue の zero-ETL integration で、ターゲットテーブルのプロパティに所有権が記録されるようになり、同じテーブルへの二重書き込みが作成時点で検出・拒否されるようになりました。実際に競合を起こして確かめてみます。
2026.09.21

クラウド事業統括本部の石川です。AWS Glue の zero-ETL integration で、ターゲットテーブルのプロパティに所有権が記録されるようになり、2つの integration が同じテーブルへ書き込む構成を作れなくなりました。実際に競合を起こして確かめてみました。

https://aws.amazon.com/jp/about-aws/whats-new/2026/09/glue-zero-etl-ownership-conflicts/

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 をうっかり二重に作ってしまい、片方がもう片方のデータを上書きする事故が起こり得ます。今回のアップデートは、この事故を作成時点で止めるものです。

https://docs.aws.amazon.com/glue/latest/dg/zero-etl-target.html

ターゲットテーブルプロパティの所有権とは

公式ドキュメントによると、ターゲットテーブルプロパティを識別するのは「ターゲットリソースの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.comdynamodb:ExportTableToPointInTimedynamodb:DescribeTabledynamodb:DescribeExport を許可します。

ターゲット側

ターゲット側は、S3 バケットを作り、そのパスを LocationUri に指定した Glue データベース zetl_blog を作成しました。LocationUri の指定は zero-ETL の必須要件です。続いて、AWS Glue がこのデータベースへ書き込むための IAM ロールを用意し、create-integration-resource-property でターゲットリソースに関連付けます。最後に、Glue データカタログのリソースポリシーへ glue:CreateInboundIntegrationglue: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: *"
}

StatusCREATING のうちに、もう一度テーブルプロパティを見てみます。

% 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本に限られます。また、同時変更の検出だけは他の条件と扱いが異なり、作成をブロックしません。別のユーザーが同じ出力設定を更新した場合、最新を取得し直すか、そのまま進めるかを選べます。

https://docs.aws.amazon.com/glue/latest/dg/zero-etl-target.html

最後に

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も扱えません。

この記事をシェアする

AWSのお困り事はクラスメソッドへ

関連記事