How to make a UUID in DynamoDB? How to make a UUID in DynamoDB? database database

How to make a UUID in DynamoDB?


Disclaimer: I am the maintainer of the Dynamodb-mapper project

Intuitive workflow of an auto-increment key:

  1. get the last counter position
  2. add 1
  3. use the new number as the index of the object
  4. save the new counter value
  5. save the object

This is just to explain the underlying idea. Never do it this way because it's not atomic. Under certain workload, you may allocate the same ID to 2+ different objects because it's not atomic. This would result in a data loss.

The solution is to use the atomic ADD operation along with ALL_NEW of UpdateItem:

  1. atomically generate an ID
  2. use the new number as the index of the object
  3. save the object

In the worst case scenario, the application crashes before the object is saved but never risk to allocate the same ID twice.

There is one remaining problem: where to store the last ID value ? We chose:

{    "hash_key"=-1, #0 was judged too risky as it is the default value for integers.    "__max_hash_key__y"=N}

Of course, to work reliably, all applications inserting data MUST be aware of this system otherwise you might (again) overwrite data.

the last step is to automate the process. For example:

When hash_key is 0:    atomically_allocate_ID()actual_save()

For implementation details (Python, sorry), see https://bitbucket.org/Ludia/dynamodb-mapper/src/8173d0e8b55d/dynamodb_mapper/model.py#cl-67

To tell you the truth, my company does not use it in production because, most of the time it is better to find another key like, for the user, an ID, for a transaction, a datetime, ...

I wrote some examples in dynamodb-mapper's documentation and it can easily be extrapolate to Node.JS

If you have any question, feel free to ask.


Another approach is to use a UUID generator for primary keys, as these are highly unlikely to clash.

IMO you are more likely to experience errors consolidating primary key counters across highly available DynamoDB tables than from clashes in generated UUIDs.

For example, in Node:

npm install uuid

var uuid = require('uuid');// Generate a v1 (time-based) iduuid.v1(); // -> '6c84fb90-12c4-11e1-840d-7b25c5ee775a'// Generate a v4 (random) iduuid.v4(); // -> '110ec58a-a0f2-4ac4-8393-c866d813b8d1'

Taken from SO answer.


If you're okay with gaps in your incrementing id, and you're okay with it only roughly corresponding to the order in which the rows were added, you can roll your own: Create a separate table called NextIdTable, with one primary key (numeric), call it Counter.

Each time you want to generate a new id, you would do the following:

  • Do a GetItem on NextIdTable to read the current value of Counter --> curValue
  • Do a PutItem on NextIdTable to set the value of Counter to curValue + 1. Make this a conditional PutItem so that it will fail if the value of Counter has changed.
  • If that conditional PutItem failed, it means someone else was doing this at the same time as you were. Start over.
  • If it succeeded, then curValue is your new unique ID.

Of course, if your process crashes before actually applying that ID anywhere, you'll "leak" it and have a gap in your sequence of IDs. And if you're doing this concurrently with some other process, one of you will get value 39 and one of you will get value 40, and there are no guarantees about which order they will actually be applied in your data table; the guy who got 40 might write it before the guy who got 39. But it does give you a rough ordering.

Parameters for a conditional PutItem in node.js are detailed here. http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/frames.html#!AWS/DynamoDB.html. If you had previously read a value of 38 from Counter, your conditional PutItem request might look like this.

var conditionalPutParams = {    TableName: 'NextIdTable',    Item: {        Counter: {            N: '39'        }    },    Expected: {        Counter: {            AttributeValueList: [                {                    N: '38'                }            ],            ComparisonOperator: 'EQ'        }    }};