I am implementing a feature called default naming for one of our product and I would like to see how conditional putItem can help me achieve our business logic need.
Here is the problem:
We have a legacy dynamoDB table which stores some entity information. The schema is: entityId, name, manufacturer
entityId is primary key and it is actually an UUID, name and manufacturer are just attributes.
Now our feature needs to generate a default name when we insert a new entry to the table. The default name needs to be unique per manufacturer, and we would like to use sequential number to represent it.
E.g. In the table we have 2 existing items
123, lucy, apple
3672, tom, apple
now if I want to insert another entry of manufacturer apple to the table, I need to generate “third” as the name attribute
5678, third, apple
It seems easy, I can just do a read on all the entities of apple and get a count, then use count+1 as the default value for the name attribute.
However, our system is distributed, multiple request may come in, and race condition may happen. If I don’t do any prevention. Two requests may insert two entries into the table
5678, third, apple
9374, third, apple
instead we actual wants them to be
5678, third, apple
9374, forth, apple
or
5678, forth, apple
9374, third, apple
To solve the problem what I am thinking is using putItem with ConditionalExpression. For the above example, first do a read, got the count 2, then if two concurrent requests try to insert
"5678, third, apple" and "9374, third, apple" into the table, then one of them should got an exception, and we can increment the count again and retry.
The code of would be
try {
Expected expect = new Expected("name").notExist();
PutItemSpec putItemSpec = new PutItemSpec().withItem(item).withExpected(expected);
entityTable.putItem(putItemSpec);
} catch (ConditionalExpression ex) {
.. increment count and do retry
}
However I found that doesn’t work, there is no exception thrown. Am I misunderstanding the conditional put? How can I accomplish my goal?