DynamoDBとPythonでユニークな連番を採番する

RDBには一意な連番を振り出す機能があります。
例えばOracleではシーケンスオブジェクト、MySQLならAuto Incrementで実装されています。

絶対に重複することがないことが保証された連番は一意なIDを振ったりするのに便利なのでシステム設計で重宝します。

NoSQLデータベースのDynamoDBではアトミックカウンタという機能を使うと連番を採番できます。

Pythonスクリプトでほんとに一意な連番が採番できるか試してみました。

ソースコード

import boto3
import time
import asyncio

dynamodb = boto3.resource('dynamodb')
table_name = 'sequence'

# テーブル作成
try:
    res = dynamodb.create_table(
        AttributeDefinitions = [
            {
                'AttributeName': 'sequence_key',
                'AttributeType': 'S'
            },
        ],
        TableName = table_name,
        KeySchema = [
            {
                'AttributeName': 'sequence_key',
                'KeyType': 'HASH'
            },
        ],
        ProvisionedThroughput = {
            'ReadCapacityUnits': 3,
            'WriteCapacityUnits': 3
        }
    )

    # テーブルができるまでスリープ
    time.sleep(10)
except Exception as e:
    pass

table = dynamodb.Table(table_name)

# sequenceをカウントアップする関数
async def countup(task_name, wait, sequence_key):
    for i in range(5):
        res = table.update_item(
            Key= {
                'sequence_key': sequence_key
                },
            UpdateExpression="ADD #name :increment",
            ExpressionAttributeNames={
                '#name':'sequence'
                },
            ExpressionAttributeValues={
                ":increment": int(1)
                },
            ReturnValues="UPDATED_NEW"
        )

        print('Task:' + task_name + ',sequence_key:' + sequence_key + ',sequence:' + str(res['Attributes']['sequence']))
        await asyncio.sleep(wait)

# カウントアップ処理を並列実行
loop = asyncio.get_event_loop()
result = loop.run_until_complete(asyncio.gather(
    countup("A", 0.5, 'Key1'),
    countup("B", 0.5, 'Key2'),
    countup("C", 1, 'Key1'),
    countup("D", 1, 'Key2'),
    countup("E", 1.5, 'Key1'),
    countup("F", 1.5, 'Key2'),
))

実行結果

>python atomic_counter_tester.py
Task:A,sequence_key:Key1,sequence:1
Task:B,sequence_key:Key2,sequence:1
Task:C,sequence_key:Key1,sequence:2
Task:D,sequence_key:Key2,sequence:2
Task:E,sequence_key:Key1,sequence:3
Task:F,sequence_key:Key2,sequence:3
Task:A,sequence_key:Key1,sequence:4
Task:B,sequence_key:Key2,sequence:4
Task:A,sequence_key:Key1,sequence:5
Task:B,sequence_key:Key2,sequence:5
Task:C,sequence_key:Key1,sequence:6
Task:D,sequence_key:Key2,sequence:6
Task:A,sequence_key:Key1,sequence:7
Task:B,sequence_key:Key2,sequence:7
Task:E,sequence_key:Key1,sequence:8
Task:F,sequence_key:Key2,sequence:8
Task:A,sequence_key:Key1,sequence:9
Task:C,sequence_key:Key1,sequence:10
Task:B,sequence_key:Key2,sequence:9
Task:D,sequence_key:Key2,sequence:10
Task:E,sequence_key:Key1,sequence:11
Task:C,sequence_key:Key1,sequence:12
Task:F,sequence_key:Key2,sequence:11
Task:D,sequence_key:Key2,sequence:12
Task:C,sequence_key:Key1,sequence:13
Task:D,sequence_key:Key2,sequence:13
Task:E,sequence_key:Key1,sequence:14
Task:F,sequence_key:Key2,sequence:14
Task:E,sequence_key:Key1,sequence:15
Task:F,sequence_key:Key2,sequence:15


ちゃんと重複なく連番が採番されることが確認できました。