Node.js
DynamoDB

DynamoDBでアトミックカウンタを使ってシーケンスを管理する

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);
        }
      });
    });
  }