0
votes

Our team has a DDB table that contains items with a 'state' attribute denoting various lifecycle states. We have another table that keeps track of the counts of these items by state (the partition key is the id of a container type a set items are grouped under, sort key is the state). Currently we use a lambda to keep the count in sync triggered by ddb stream on the table containing the items, but we've had some problems with this (latency, idempotency) and are looking into using DynamoDB transactions as a way to update the count table synchronously whenever we change the state of an item.

I was wondering if there is a way to do an atomic, incremental add using AttributeValueUpdate + 'AttributeAction.ADD' like in this example but within a DynamoDB Transaction so that the update to item state and count increment will be an ACID, all-or-nothing operation? The approach in the example uses an UpdateItemRequest with AttributeValueUpdate, while it seems like Transactions can only use the Update object, and I haven't been able to find a way to use AttributeValueUpdate with that object. here

From my understanding of Transactions, there is no ordering to the independent operations so no operation can depend on the output of another (I can't read the value then add to it in same transaction) and if I read the value first it may be changed by the time the transaction occurs. I haven't been able to find any other example of incrementing a value within a DynamoDB transaction, hoping someone can tell me if this is possible.

1

1 Answers

1
votes

This is definitely possible, you just don't have access to the fluent API that UpdateItemRequest uses to construct the update expression (via AttributeValueUpdate). Instead, you have to write the update expression by hand.

In the example you linked, the following:

UpdateItemRequest updateRequest =
    new UpdateItemRequest().withTableName("local-content")
        .withKey(key)
        .addAttributeUpdatesEntry("views", new AttributeValueUpdate()
            .withValue(new AttributeValue().withN("" + delta))
            .withAction(AttributeAction.ADD));

Is equivalent to something like this:

// Create a map of expression attribute values with the amount to increment by
Map<String, AttributeValue> values = new HashMap<String, AttributeValue>();
values.put(":one", new AttributeValue().withN("1"));

Update update =
    new Update().withTableName("local-content")
        .withKey(key)
        .withExpressionAttributeValues(values)
        .withUpdateExpression("ADD views :one");

With that said, keep in mind that DynamoDB transactions are significantly more expensive than non-transactional operations, and what you're trying to do will likely fall apart at high throughputs, as the counters will become hot keys.