Node.js での DynamoDB BatchWriteItem(AWS SDK v3)
BatchWriteItem は 1 リクエストで 最大 25 件のアイテム を put または delete します。これは小さな UpdateItem ではありません。どの PutRequest も保存されているアイテムを丸ごと置き換えますし、v3 の型には条件を付けられる場所がありません。上限と部分的失敗のモデルは DynamoDB のバッチ操作で扱っています。このページは v3 の呼び出しと、それが静かにデータを失う唯一の経路についてです。
コード
import {BatchWriteItemCommand, DynamoDBClient} from '@aws-sdk/client-dynamodb';
const client = new DynamoDBClient({region: 'us-east-1'});
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
let requestItems = {
Music: [
{
PutRequest: {
Item: {
Artist: {S: 'Arturo Sandoval'},
SongTitle: {S: 'Cubano Chant'},
AlbumTitle: {S: 'Danzon'},
Year: {N: '1994'}
}
}
},
{
PutRequest: {
Item: {
Artist: {S: 'Arturo Sandoval'},
SongTitle: {S: 'A Mis Abuelos'},
AlbumTitle: {S: 'Danzon'},
Year: {N: '1994'}
}
}
},
{
DeleteRequest: {
Key: {Artist: {S: 'Ella Fitzgerald'}, SongTitle: {S: 'Misty'}}
}
}
]
};
let attempt = 0;
do {
const response = await client.send(new BatchWriteItemCommand({RequestItems: requestItems}));
// Writes that were throttled come back in UnprocessedItems — resubmit them
// with exponential backoff until the map is empty.
requestItems = response.UnprocessedItems;
if (requestItems && Object.keys(requestItems).length > 0) {
attempt += 1;
await sleep(Math.min(100 * 2 ** attempt, 5000));
}
} while (requestItems && Object.keys(requestItems).length > 0);
console.log('Batch written');解説
- 残りが入るメンバーは
UnprocessedKeysではなくUnprocessedItemsです。読み取り側はもう一方の名前を使います。そして JavaScript ではここでのタイプミスがコンパイルを通り、undefinedとして読まれ、do/whileを 1 回で終わる呼び出しに変えて、スロットリングされた書き込みを取りこぼします。TypeScript は捕まえますが、素の JS は捕まえません。 - 条件を置く場所がありません。v3 の
WriteRequest型が持つ省略可能なメンバーはちょうど 2 つ、PutRequestとDeleteRequestだけで、どちらもConditionExpressionやReturnValuesを受け付けません。これは SDK が慎重なのではありません。API リファレンスが、個々の put および delete リクエストに条件は指定できないと述べています。ガードが必要な書き込みはバッチの仕事ではなく、条件付きの UpdateItem かトランザクションの仕事です。 catchできる間違いは 2 つ、どちらもリトライ不可 で、err.nameで識別します。26 件のエントリはValidationException/Too many items requested for the BatchWriteItem callを投げます。1 つのキーに 2 回触れるとProvided list of item keys contains duplicatesが出ますが、このメッセージは 2 つの put だけでなく put と delete の組み合わせも指すため、初めて見ると妙に読めます。- バッチ全体が拒否される条件は、目につく 3 つより多くあります。25 件超のリクエスト、400 KB 超のアイテム、合計 16 MB 超に加えて、存在しないテーブル、スキーマに合わないキー、2048 バイトを超えるパーティションキー、1024 バイトを超えるソートキーでも DynamoDB はバッチを拒否します。1 件の不正なエントリで 25 件すべてを失います。
- バッチが節約するのはラウンドトリップであってキャパシティではありません。各エントリは個別の
PutItemまたはDeleteItemとして 1 KB 単位に切り上げて課金され、存在しないアイテムへの delete でも書き込みユニットを消費します。
キーだけの PutRequest はアイテムの残りを破壊します
Ella Fitzgerald / Misty は最初 AlbumTitle と Year を持っています。ここで 2 つのキー属性だけを載せた PutRequest を 1 つ送ります。
{PutRequest: {Item: {Artist: {S: 'Ella Fitzgerald'}, SongTitle: {S: 'Misty'}}}}その後 ConsistentRead: true で読み戻します。DynamoDB Local 3.3.0 はこう返します。
{
"Artist": { "S": "Ella Fitzgerald" },
"SongTitle": { "S": "Misty" }
}AlbumTitle と Year は消えました。呼び出しは成功し、UnprocessedItems は {} で、レスポンスのどこにも落とした 2 つの属性への言及はありません。put はアイテム全体の置き換えなので、部分的なペイロード(API リクエストのボディ、CSV の一部の列、属性を省いた射影付き Query の結果)から組み立てたバッチは、そのペイロードが運ばなかった属性をすべて消し去ります。
更新のつもりでバッチを使うときに備えるべき失敗モードがこれです。対策は、現在のアイテムを先に読んでマージするか、バッチをやめて、名前を挙げた属性だけに触れる UpdateItem を使うことです。
25 件のバッチが 12 件のバッチになるもう 1 つの理由はサイズです。書き込みは課金上それぞれ 1 KB 単位に切り上げられ、リクエストは 16 MB で頭打ちになるので、アイテムの実際のバイト数が請求額と収まる件数の両方を決めます。アイテムサイズ計算ツールは、配列を組み立てる前にアイテムごとのその数字を教えてくれます。
置き換えのセマンティクスを手書きせずにアイテムをまとめて読み込み、編集し、削除するには、DynoTable をダウンロードしてください。
関連する例
- Python での DynamoDB バッチ書き込み — boto3 の
batch_writer()がリトライループを代わりにやってくれます。 - AWS CLI での DynamoDB BatchWriteItem — シェルからの同じバッチ書き込み。
- Node.js での DynamoDB TransactWriteItems — 書き込みがまとめて成功または失敗しなければならない場合。
- DynamoDB のバッチ操作 — 上限、部分的な失敗、そしてバッチ化が報われる場面。
- 「Too many items requested for the BatchWriteItem call」 — 1 つのバッチに 25 件を超える put/delete リクエスト。
- 「Provided list of item keys contains duplicates」 — 1 つのバッチで同じキーに触れる 2 つのリクエスト。
参考資料
- BatchWriteItem — Amazon DynamoDB API Reference
- Error handling with DynamoDB — Amazon DynamoDB Developer Guide
- DynamoDB read and write operations (capacity unit consumption) — Amazon DynamoDB Developer Guide
最終検証日 2026-07-28、上記にリンクした公式 AWS ドキュメントに照らして確認しました。