「ERROR: canceling statement due the statement timeout」というエラーメッセージが表示される AWS DMS タスクのトラブルシューティング方法を教えてください。

最終更新日: 2019 年 9 月 30 日

AWS Database Migration Service (AWS DMS) を使用して、オンプレミスの PostgreSQL データベースとの間でデータを移行しています。AWS DMS タスクはしばらくの間正常に実行されますが、それからエラーで失敗します。このエラーをトラブルシューティングして解決するにはどうすれば良いですか?

簡単な説明

PostgreSQL データベースが移行タスクのソースである場合、AWS DMS は全ロードフェーズでテーブルからデータを取得します。その後、AWS DMS は、変更データキャプチャ (CDC) フェーズで、レプリケーションスロットによって保持されている先書きログ (WAL) から読み込みます。

PostgreSQL データベースが移行タスクのターゲットである場合、AWS DMS はソースからデータを取得し、レプリケーションインスタンスに CSV ファイルを作成します。次に、AWS DMS は全ロードフェーズで COPY コマンドを実行し、それらのレコードをターゲットに挿入します。ただし、トランザクション適用モードでは、AWS DMS は CDC フェーズでソースの WAL ログから正確な DML ステートメントを実行します。バッチ適用モードの場合も、AWS DMS は CDC フェーズで CSV ファイルを作成し、COPY コマンドを実行してターゲットに差分変更を挿入します。

AWS DMS がソースからデータを取得するか、ターゲットにデータを配置するコマンドを実行する際、AWS DMS はデフォルトのタイムアウト設定である 60 秒を使用します。ソースまたはターゲットが高負荷であるか、テーブルにロックがある場合には、AWS DMS は 60 秒以内にこれらのコマンドの実行を終了できません。その結果、タスクは失敗して、「canceling statement due to statement timeout (ステートメントタイムアウトが原因でステートメントをキャンセルする)」というエラーになり、ログに次のいずれかのエントリが表示されます。

メッセージ
]E:  RetCode: SQL_ERROR  SqlState: 57014 NativeError: 1 Message: ERROR: canceling statement due to statement timeout;

これらのエラーをトラブルシューティングして解決するには、以下の手順を実行します。

  • コマンドの実行時間が長くなる原因を特定します。
  • タイムアウト値を増やし、スロット作成タイムアウト値を確認します。
  • スロット作成の問題をトラブルシューティングします。

解決方法

コマンドの実行時間が長くなる原因を特定する

タイムアウト期間中に実行できなかったコマンドを見つけるには、AWS DMS タスクログタスクのテーブル統計セクションを確認します。この情報は、パラメータ log_min_error_statementERROR またはより低い重要度に設定されている場合は、PostgreSQL のエラーログファイルでも確認できます。失敗したコマンドを特定すると、失敗したテーブル名を見つけることができます。PostgreSQL エラーログの次のエラーメッセージ例を参照してください。

ERROR: canceling statement due to statement timeout 
STATEMENT: <The statement executed>"

関連付けられたテーブルのロックを見つけるには、ソースまたはターゲットで次のコマンドを実行します (エラーが表示される場所によって異なります)。

SELECT blocked_locks.pid     AS blocked_pid,
         blocked_activity.usename  AS blocked_user,
         blocking_locks.pid     AS blocking_pid,
         blocking_activity.usename AS blocking_user, 
         blocked_activity.query    AS blocked_statement,
         blocking_activity.query   AS current_statement_in_blocking_process
   FROM  pg_catalog.pg_locks         blocked_locks 
    JOIN pg_catalog.pg_stat_activity blocked_activity  ON blocked_activity.pid = blocked_locks.pid
    JOIN pg_catalog.pg_locks         blocking_locks 
        ON blocking_locks.locktype = blocked_locks.locktype 
        AND blocking_locks.DATABASE IS NOT DISTINCT FROM blocked_locks.DATABASE
        AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation
        AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page
        AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple
        AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid
        AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid
        AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid
        AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid
        AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid
        AND blocking_locks.pid != blocked_locks.pid 
    JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid
   WHERE NOT blocked_locks.GRANTED;

ブロックされている PID が見つかった場合は、次のコマンドを実行して、ブロックされた PID を停止つまり「強制終了」します。

SELECT pg_terminate_backend(blocking_pid); 

デッド行または「タプル」は SELECT 時間を増やす可能性があるため、次のコマンドを実行してソーステーブルに多数のデッド行がないか確認します。

select * from pg_stat_user_tables where relname= 'table_name';

失敗したターゲットテーブルにプライマリキーまたは一意のインデックスがあるかどうかを確認します。プライマリキーまたは一意のインデックスがない場合、UPDATE ステートメントの実行中にテーブル全体のスキャンが行われるため、長い時間がかかります。

タイムアウト値を増やす

AWS DMS は、ソースエンドポイントとターゲットエンドポイントの両方で、executeTimeout という追加の接続属性を使用します。executeTimeout のデフォルト値は 60 秒であるため、クエリの実行に 60 秒以上かかる場合、AWS DMS はタイムアウトします。

エラーが Source_Unload または Source_Capture に表示される場合は、ソースの executeTimeout のタイムアウト値を設定します。エラーが Target_Load または Target_Apply に表示される場合は、ターゲットの executeTimeout のタイムアウト値を設定します。以下の手順に従って、タイムアウト値の設定を増やします。

1.    AWS DMS コンソールを開きます。

2.    ナビゲーションペインから [ Endpoints (エンドポイント)] を選択します。

3.    PostgreSQL エンドポイントを選択します。

4.    [Actions (アクション)] を選択し、[Modify (変更)] を選択します。

5.    [Endpoint-specific settings (エンドポイント特有の設定)] セクションを展開します。

6.    [Extra connection attributes (追加の接続属性)] のフィールドに、次の値を入力します。

executeTimeout=3600;

7.    [Save] を選択します。

8.    [Endpoints (エンドポイント)] ペインで、PostgreSQL エンドポイントの名前を選択します。

9.    [Connections (接続)]セクションで、エンドポイントの[Status (ステータス)] が Testing から Successful に変わります。

PostgreSQL DB インスタンスの statement_timeout パラメータを増やします (ミリ秒単位)。デフォルト値は 0 で、クエリのタイムアウトは無効になります。lock_timeout パラメータを増やすこともできます。デフォルト値は 0 で、ロックのタイムアウトは無効になります。

スロット作成の問題のトラブルシューティング

PostgreSQL データベースでレプリケーションスロットを作成したときにタイムアウトが発生した場合は、次のようなログエントリが表示されます。

メッセージ
]E:  wal_slot_create(...) - Unable to create slot 'xxxxxxxxxxxxxxxx_00016391_c4a70947_84c9_4a55_8d54_ff63f2f69a52' (on execute(...) phase) [1020101]  (postgres_endpoint_wal_utils.c:3215)

レプリケーションインスタンスでバージョン 3.1.3 以前が実行されている場合、このコマンドにはデフォルトのタイムアウト設定の 60 秒が適用されます。executeTimeout の値は上書きされます。この問題を解決するには、このコマンドのデフォルトタイムアウトが 600 秒であるバージョン 3.1.4 を使用してください。このタイムアウトは、[Task settings (タスク設定)] セクションの TransactionConsistencyTimeout パラメータを設定することで増やすことができます。

データベースユーザーテーブルにアクティブなロックがある場合、PostgreSQL はレプリケーションスロットを作成できません。次のコマンドを実行して、ロックを確認してください。

select * from pg_locks;

それから、エラーが解決されたかどうかをテストするため、次のコマンドを実行して、ソースの PostgreSQL データベースにレプリケーションスロットを手動で作成します。

select  xlog_position FROM pg_create_logical_replication_slot('<Slot name as per
    the task log>', 'test_decoding');

それでもコマンドでスロットを作成できない場合は、PostgreSQL DBA を操作してボトルネックを特定し、データベースを設定する必要がある場合があります。コマンドが成功したら、テストとして作成したスロットは削除してください。

select pg_drop_replication_slot(‘<slot name>');

最後に、移行タスクを再開します。