AWS Developer Tools Blog

Specifying Conditional Constraints with Amazon DynamoDB Mapper

Conditional constraints are a powerful feature in the Amazon DynamoDB API. Until recently, there was little support for them in the Amazon DynamoDB Mapper. You could specify a version attribute for your mapped objects, and the mapper would automatically apply conditional constraints to give you optimistic locking, but you couldn’t explicitly specify your own custom conditional constraints with the mapper.

We’re excited to show you a new feature in the DynamoDB Mapper that lets you specify your own conditional constraints when saving and deleting data using the mapper. Specifying conditional constraints with the mapper is easy. Just pass in a DynamoDBSaveExpression object that describes your conditional constraints when you call DynamoDBMapper.save. If the conditions are all met when they’re evaluated on the server side, then your data will be saved, but if any of the conditions are not met, you’ll receive an exception in your application, letting you know about the conditional check failure.

Consider an application with a fleet of backend workers. When a worker starts processing a task, it marks the task’s status as IN_PROGRESS in a DynamoDB table. We want to prevent the case where multiple workers start working on the same task. We can do that easily with the new support for conditional constraints in the DynamoDB Mapper. When a worker attempts to set a task to IN_PROGRESS, the mapper simply adds a constraint that the previous state for the task should be READY. That way, if two workers try to start working on the task at the same time, the first one will be able to set it’s status to IN_PROGRESS and start processing the task, but the second worker will receive a ConditionalCheckFailedException since the status field wasn’t what it expected when it saved its data.

Here’s what the code looks like:

try {
   DynamoDBSaveExpression saveExpression = new DynamoDBSaveExpression();
   Map expected = new HashMap();
   expected.put("status", 
      new ExpectedAttributeValue(new AttributeValue("READY").withExists(true));

   saveExpression.setExpected(expected);

   mapper.save(obj, saveExpression);
} catch (ConditionalCheckFailedException e) {
   // This means our save wasn't recorded, since our constraint wasn't met
   // If this happens, the worker can simply look for a new task to work on
}