中級読了 1 分

DynamoDBのReturnValues:書き込みから古いまたは新しいアイテムを取得する

デフォルトでは、DynamoDBの書き込みは成功以外に何も返しません。しかし、書き込みの 周辺のデータ — 変更前の値や、変更後の新しい値 — が必要になることはよくあります。 素朴な解決策は2回目のGetItemですが、これは余分な往復であり、競合でもあります: その間に別の誰かが書き込めるのです。DynamoDBは**ReturnValues**パラメータで両方を 回避します。これは書き込み自体の一部として、古いまたは新しいアイテムをアトミックに 返します。

DynamoDBのReturnValuesは何をするのか?

ReturnValuesは、DynamoDBの書き込みに対して同じ呼び出しの中でアイテムを返すよう指示します。これにより、2回目のGetItemとそれが生む競合状態を回避できます。PutItemDeleteItemNONEまたはALL_OLDのみを受け付け、UpdateItemは5つすべて(NONEALL_OLDUPDATED_OLDALL_NEWUPDATED_NEW)を受け付け、古いまたは新しい値をアトミックに返します。

  • ReturnValuesは書き込みの一部としてアイテムを返す — 2回目の読み取りも、競合もなし。
  • NONE(デフォルト) — 何も返さない。
  • ALL_OLD — 書き込み前の状態のアイテム全体。
  • UPDATED_OLD — 更新が変更した属性のみの、変更前の値。
  • ALL_NEW — 書き込み後のアイテム全体。
  • UPDATED_NEW — 変更された属性のみの、変更後の値。
  • PutItem/DeleteItemNONEまたはALL_OLDのみを受け付け、 UpdateItemは5つ すべてを受け付ける。

問題:今上書きした値が必要

サポートデスクを運用していて、担当者がチケットのステータスをopenからpendingに 変更するとします。監査ログには、変更前のステータスが何だったかを記録する必要が あります。ReturnValuesがなければ、こうするでしょう:

  1. 現在のステータスを読み取るGetItem
  2. 新しいものを設定するUpdateItem

ステップ1と2の間に別の担当者がステータスを変更するかもしれません — すると監査ログは 古い「変更前」の値を記録してしまいます。さらに悪いことに、1つの論理的な操作に対して 2回の呼び出しです。ReturnValuesはこれを、書き込み時点で実際にそうだった古いステータスを 返す、単一のアトミックなUpdateItemにまとめます。

5つの選択肢と、それぞれの使いどころ

UpdateItemはすべてをサポートします。選択は、アイテムのどの部分書き込みのどちら側が 必要かです:

ReturnValues返すもの使う場面
NONEなしアイテムを返す必要がない(デフォルト)
ALL_OLDアイテム全体、書き込み前監査 / 「今何を置き換えた?」
UPDATED_OLD変更した属性、書き込み前触れたフィールドだけ気にする場合
ALL_NEWアイテム全体、書き込み後呼び出し元に返す新しい完全なアイテムが必要な場合
UPDATED_NEW変更した属性、書き込み後今インクリメントしたカウンター/値を読み戻す場合

UPDATED_NEWは日常の主役です:更新式でカウンターを インクリメントし、同じ呼び出しで新しい合計を読み戻します。競合はありません。サポート チケットの監査では、ALL_OLD(ステータスフィールドだけをログに記録するなら UPDATED_OLD)が変更前の状態をアトミックに捉えます。

非対称性に注目してください:PutItemDeleteItemNONEALL_OLDのみをサポート します — deleteには返すべき「新しい」値がなく、putの新しい値はまさに送ったものだから です。その場で変更するUpdateItemだけが5つすべてを提供します。 AWSのドキュメント に正確な対応表があります。

DynoTableでの更新の書き込み

DynamoDB式ビルダーUpdateItemとその更新式を ビジュアルに組み立てましょう — SET/ADD句に加えて属性名と値のマップを出力します。 アプリ内では、ステージした書き込みがコミットされた後にDynoTableが結果のアイテムを表示 するので、新しい状態を直接確認できます。

DynoTable でアイテムのステージ済み変更を確認しているところ — 更新がコミットされる前の古い値と新しい値が表示されている。
DynoTable でアイテムのステージ済み変更を確認しているところ — 更新がコミットされる前の古い値と新しい値が表示されている。

落とし穴と次のステップ

  • 変更の周辺を読むためにGetItemしてから書き込んではいけない — 往復であり競合です。 ReturnValuesを使いましょう。
  • UPDATED_*は触れた属性のみを返す — アイテム全体が必要ならALL_*を使いましょう。
  • PutItem/DeleteItemは新しい値を返せないNONE/ALL_OLDのみです。
  • ReturnValuesは条件の代わりにはならない — 書き込みをガードするには 条件式を追加し、その効果を読み戻すには ReturnValuesを使いましょう。両者は組み合わせられます。
  • 関連: 更新式アトミックカウンター

2回の呼び出しをスクリプトで書かずに編集を行い、変更前後を確認したいですか? DynoTableをダウンロードして、アイテムを直接編集してください。

更新日