1. ホーム
  2. amazon-dynamodb

[解決済み] DynamoDB put-item ConditionalCheckFailedException

2022-02-02 16:25:28

質問

以下のテーブルスキーマが定義されている場合( create-table.json を呼び出した後、次のようなエラーが発生します。 put-item を使って add-event1.json に続いて add-event2.json :

PutItem オペレーションの呼び出し時に、クライアントエラー(ConditionalCheckFailedException)が発生しました。条件付きリクエストに失敗しました

なぜConditionExpressionでは両方のレコードを書き込めないのですか?(2回目の操作の後に2つのレコードがあることを期待しています)。

キー以外の条件を使用しているためではないかと疑いましたが、何も ドキュメント は、非キーコンディションをサポートしていないことを示すものです。

テーブルの作成

$ aws dynamodb create-table --cli-input-json file://create-table.json

create-table.json

{
    "TableName": "EVENTS_TEST",
    "KeySchema": [
      { "AttributeName": "aggregateId", "KeyType": "HASH" },
      { "AttributeName": "streamRevision", "KeyType": "RANGE" }
    ],
    "AttributeDefinitions": [
      { "AttributeName": "aggregateId", "AttributeType": "S" },
      { "AttributeName": "streamRevision", "AttributeType": "N" }
    ],
    "ProvisionedThroughput": {
      "ReadCapacityUnits": 10,
      "WriteCapacityUnits": 10
    }
 }

最初のレコードを追加する

$ aws dynamodb put-item --cli-input-json file://add-event1.json

追加イベント1.json

{
  "TableName": "EVENTS_TEST",
  "Item": {
    "aggregateId": { "S": "id" },
    "id": { "S": "119" },
    "context": { "S": "*" },
    "aggregate": { "S": "*" },
    "streamRevision": { "N": "0" },
    "commitId": { "S": "1119" },
    "commitSequence": { "N": "0" },
    "commitStamp": { "N": "1470185631511" },
    "dispatched": { "BOOL": false },
    "payload": { "S": "{ \"event\": \"bla\" }" }
  },
  "ExpressionAttributeNames": { "#name": "aggregate" },
  "ConditionExpression": "attribute_not_exists(aggregateId) and attribute_not_exists(streamRevision) and #name <> :name and context <> :ctx",
  "ExpressionAttributeValues": {
    ":name": { "S": "*" },
    ":ctx": { "S": "*" }
  }
}

2つ目のレコードを追加する

$ aws dynamodb put-item --cli-input-json file://add-event2.json

追加イベント2.json

{
  "TableName": "EVENTS_TEST",
  "Item": {
    "aggregateId": { "S": "id" },
    "id": { "S": "123" },
    "context": { "S": "myCtx" },
    "aggregate": { "S": "myAgg" },
    "streamRevision": { "N": "0" },
    "commitId": { "S": "1123" },
    "commitSequence": { "N": "0" },
    "commitStamp": { "N": "1470185631551" },
    "dispatched": { "BOOL": false },
    "payload": { "S": "{ \"event\": \"bla2\" }" }
  },
  "ExpressionAttributeNames": { "#name": "aggregate" },
  "ConditionExpression": "aggregateId <> :id and streamRevision <> :rev and #name <> :name and context <> :ctx",
  "ExpressionAttributeValues": {
     ":id": { "S": "id" },
     ":rev": { "N": "0" },
     ":name": { "S": "myAgg" },
     ":ctx": { "S": "myCtx" }
  }
}

解決方法は?

あなたの目標は、両方のレコードを保存することです。ここで2つの問題があります

  1. ハッシュとレンジキーの選択で、両方のレコードを保存することは不可能です。

ハッシュキーとレンジキーの組み合わせで、レコードは一意になります。 イベント1とイベント2は、ハッシュキーとレンジキーが同じ値になっています。 したがって、2番目のput-itemは最初のレコードを単純に上書きすることになります。

  1. あなたのConditionExpressionは、レコード1からレコード2への置き換えを防止します。

ConditionExpressionは、レコードを置く直前に評価されます。イベント2を挿入しようとしたときに、aggregateId "id1" を持つレコードがすでに存在することが判明したため、式は失敗します。この条件は "attribute_not_exists(aggregateId)" で失敗し、ConditionalCheckFailedException を受け取ります。 この式は、レコード2によるレコード1の上書きを防止します。

もし両方のレコードを保存したい場合は、データ項目の単一性をよりよく表すハッシュキーや範囲キーの別の選択を考え出す必要があります。ConditionExpression でそれを解決することはできません。