DynamoDBを用いていると、IDなんかをシーケンシャルに扱いたくなってくることがよくあります。
いわゆる連番IDというやつですね。
一見DynamoDBでシーケンシャルなIDを管理しようと思うと、シーケンステーブルを作って最大値を保存するレコードを作成して、それを get
して += 1
して update
すれば実現出来る……と考えてしまいますが、それではトランザクションの原子性を保証することが出来なくて不安になってしまいます。同時に別のユーザがこの処理を実行してしまったらどうなってしまうのでしょうか!
問題ありません! DynamoDBではアトミックカウンタを使って一つのクエリでレコードの現在の値を更新しつつ結果を取得する事が出来ます!
公式ドキュメントは次の通りです。
https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/WorkingWithItems.html#WorkingWithItems.AtomicCounters
Node.jsでの実装の一例を以下に示します。
実装例のシーケンステーブルは、パーティションキーにシーケンスID、 value
に実際の最大値を想定しています。
/**
* @param {string} tableName
* @param {Object<string, *>} key
* @param {number} incr
* @return {Promise.<DocumentClient.AttributeMap>}
*/
function incrementalUpdate(tableName, key, incr) {
/**
*
* @type {DocumentClient.UpdateItemInput}
*/
const params = {
TableName: tableName,
Key: key,
ReturnValues: "ALL_NEW",
UpdateExpression: "SET #value = #value + :incr",
ExpressionAttributeNames: {
["#value"]: "value"
},
ExpressionAttributeValues: {
[":incr"]: incr
}
};
console.log("INCREMENTAL_UPDATE", params);
return new Promise((resolve, reject) => {
dynamoDBDocumentClient.update(params, (err, data) => {
if (err) {
return reject(err);
} else {
return resolve(data.Attributes);
}
});
});
}