AWS CLI での DynamoDB 条件付き書き込み
条件付き書き込みはシェルから送るのは簡単ですが、読む のは厄介です。失敗したときの面白い結果が、出力ではなくエラーとして届くからです。DynamoDB の条件式は式で何を表現できるかを扱います。このページは、それを CLI から実行し、負けたアイテムを失敗の中から取り出す方法についてです。
コード
aws dynamodb update-item \
--table-name 'Music' \
--key '{"Artist":{"S":"Arturo Sandoval"},"SongTitle":{"S":"Cubano Chant"}}' \
--update-expression 'SET #upd0 = :updValue0, #version = :newVersion' \
--condition-expression 'attribute_exists(#cond0) AND #version = :expectedVersion' \
--expression-attribute-names '{"#upd0":"Genre","#version":"Version","#cond0":"Artist"}' \
--expression-attribute-values '{":updValue0":{"S":"Latin Jazz"},":expectedVersion":{"N":"7"},":newVersion":{"N":"8"}}'成功するとコマンドは何も出力せず、終了コード 0 で終わります。別の書き込み手が先に到達していれば条件は失敗し、CLI はサービスのメッセージを報告します。
An error occurred (ConditionalCheckFailedException) when calling the UpdateItem operation:
The conditional request failed解説
- 成功は無言。出力なし、終了コード 0。パースするものも表明するものもないので、シェルスクリプトは終了ステータスを結果として扱うしかありません。更新後のアイテムを表示したいなら
--return-values ALL_NEWを足します。 - 失敗は終了ステータス 254 で、これは CLI v2 におけるクライアント側エラーのコードであり、不正なリクエストと共有されています。リトライする前にメッセージで分岐しましょう。さもないと式のタイプミスが無限のバックオフループになります。
--return-values-on-condition-check-failure ALL_OLDはここで実際に効きます。有効な値はALL_OLDとNONEで、読み取りキャパシティは消費しません。エラーからアイテムを取り出すにはもう 1 つフラグが要ります。下で扱います。- 条件と更新は別々のフラグですが、名前空間は共有します。
--expression-attribute-namesと--expression-attribute-valuesは--update-expressionと--condition-expressionにまたがってマージされます。だからこそ生成される名前は句ごとに振り直されず#upd0、#cond0と続くのです。1 つのプレースホルダーを 2 つの異なる意味で使い回すと、後勝ちで静かに上書きされます。 - 失敗した書き込みにも課金されます。デベロッパーガイドは明確です。条件が false と評価されてもやはり書き込みキャパシティを消費し、その大きさは古いアイテムと新しいアイテムの大きい方で決まります。条件は安価な存在チェックではありません。
失敗時の出力と、そこからアイテムを取り出す方法
このフェンスを 1 回実行すると、静かに成功します。Version がもう 7 ではなくなった状態で 2 回目を実行すると、aws-cli/2.36.9 は標準エラー出力にこう表示します。
aws: [ERROR]: An error occurred (ConditionalCheckFailedException) when calling the UpdateItem operation: The conditional request failed--return-values-on-condition-check-failure ALL_OLD を足すと、デフォルトの出力は「まだ続きがある」ことだけを、中身を見せずに伝えます。
aws: [ERROR]: An error occurred (ConditionalCheckFailedException) when calling the UpdateItem operation: The conditional request failed
Additional error details:
Item: <complex value>
Use "--cli-error-format json" or another error format to see the full details.<complex value> がそのアイテムで、デフォルトのテキストレンダラーが伏せています。--cli-error-format json を足すと、全部が表示されます。
{
"Message": "The conditional request failed",
"Code": "ConditionalCheckFailedException",
"Item": {
"Artist": {"S": "Arturo Sandoval"},
"Year": {"N": "1994"},
"Version": {"N": "8"},
"SongTitle": {"S": "Cubano Chant"},
"AlbumTitle": {"S": "Danzon"},
"Genre": {"S": "Latin Jazz"}
}
}(属性マップは 1 つずつ 1 行に畳んでいます。それ以外は出力されたままです。)1 回目の実行が成功したので Version は 8 になり Genre も設定されています。これが、シェルスクリプトから閉じた楽観的ロックのループです。標準エラー出力を jq -r '.Item.Version.N' に通し、その値を :expectedVersion として戻し、リトライする。get-item は不要で、読み取りとリトライの間に第三の書き込み手が滑り込む隙間もありません。
リトライはただではありません。拒否された試行ごとに書き込みユニットを 1 つ消費するので、競合するキーをタイトループで叩くと、進捗ゼロのまま着実に課金されます。料金計算ツールは、試行回数に上限を設ける前に、リトライの嵐が実際いくらかかるのかを知りたいときに、書き込みレートを月額の数字に変えてくれます。
プレースホルダーのマップをシェルでクォートすることなく、自分のテーブルに対してこうしたガードを実行するには、DynoTable をダウンロードしてください。
関連する例
- Node.js での DynamoDB 条件付き書き込み — AWS SDK v3 での同じ楽観的ロック。
- Python での DynamoDB 条件付き書き込み — boto3 での同じ楽観的ロック。
- AWS CLI での DynamoDB PutItem — 作成のみの
attribute_not_existsput。 - DynamoDB の条件式 — すべての関数とパターン。
- ReturnValues を理解する — 各返却オプションで何が得られるか。
- DynamoDB ConditionalCheckFailedException — チェックの失敗が想定内のときに、それを安く扱う方法。
参考資料
- UpdateItem — Amazon DynamoDB API Reference
- update-item — AWS CLI Command Reference
- Condition expressions — Amazon DynamoDB Developer Guide
- DynamoDB read and write operations (capacity unit consumption) — Amazon DynamoDB Developer Guide
最終検証日 2026-07-28、上記にリンクした公式 AWS ドキュメントに照らして確認しました。