Lambda(C#),DynamoDBでAuto Increment(アトミックカウンター)する方法


DynamoDB には、他の RDBMS のようなシーケンス番号で KEY を管理する Auto Increment の機能は残念ながらありません。

ですが、それを補う方法として、シーケンス番号を管理するテーブルを作成しておき、カウントアップ更新時にカウントアップした値を返すというやり方があります。

「更新処理→更新結果を返す」間には、他の処理が入り込む余地がないためシーケンス番号を重複なく管理することが可能です。

このやり方を AWS 公式では「アトミックカウンター」と呼んでいます。

Lambda 関数(C#) で、アトミックカウンターを実装する方法は、以下のようになります。

まず、DynamoDBのItemIdというテーブルを以下のように作成します。パーティションキーはidで、_versionはキーではない属性になります。

f:id:fnyablog:20181113092915p:plain:w320

C# のコードは以下のようになります。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.Model;
using Amazon.Lambda.Core;

[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.Json.JsonSerializer))]
namespace AWSLambda1
{
    public class Function
    {
        public async Task FunctionHandler(object input, ILambdaContext context)
        {
            AmazonDynamoDBClient client = new AmazonDynamoDBClient();
            string tableName = "ItemId";

            try
            {
                var request = new UpdateItemRequest
                {
                    TableName = tableName,
                    Key = new Dictionary<string, AttributeValue>()
                    {
                        {
                            "id", new AttributeValue()
                            {
                                S = "UserId"
                            }
                        }
                    },
                    ExpressionAttributeNames = new Dictionary<string, string>()
                    {
                        { "#v", "_version" }
                    },
                    ExpressionAttributeValues = new Dictionary<string, AttributeValue>()
                    {
                        { ":incr", new AttributeValue()
                            {
                                N = "1"
                            }
                        }
                    },

                    // Update(カウントアップ)
                    UpdateExpression = "SET #v = #v + :incr",

                    ReturnValues = "ALL_NEW" // Give me all attributes of the updated item.
                };

                var response = await client.UpdateItemAsync(request);

                var attributeList = response.Attributes;

                PrintItem(attributeList);

                Console.WriteLine("Task completed.");

            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
            
        }

        private void PrintItem(Dictionary<string, AttributeValue> attributeList)
        {
            Console.WriteLine("************************************************");

            foreach (KeyValuePair<string, AttributeValue> kvp in attributeList)
            {
                string attributeName = kvp.Key;
                AttributeValue value = kvp.Value;

                Console.WriteLine(
                    attributeName + " " +
                    (value.S == null ? "" : "S=[" + value.S + "]") +
                    (value.N == null ? "" : "N=[" + value.N + "]") +
                    (value.SS == null ? "" : "SS=[" + string.Join(",", value.SS.ToArray()) + "]") +
                    (value.NS == null ? "" : "NS=[" + string.Join(",", value.NS.ToArray()) + "]")
                    );
            }

            Console.WriteLine("************************************************");
        }

    }
}

この Lambda 関数を実行した結果のログは以下のようになり、Update 後の _version 属性がカウントアップしていることが確認できます。

f:id:fnyablog:20181113093247p:plain:w640

DynamoDB を確認しても、正しく値が更新されていることが分かります。

f:id:fnyablog:20181113093333p:plain:w320

・参考書籍

AWS Lambda実践ガイド

AWS Lambda実践ガイド

・参考サイト

docs.aws.amazon.com

docs.aws.amazon.com