Wednesday, September 28, 2022
HomeJavaUtilizing atomic counters within the Enhanced DynamoDB AWS SDK for Java 2.x...

Utilizing atomic counters within the Enhanced DynamoDB AWS SDK for Java 2.x shopper


We’re happy to announce that customers of the enhanced shopper for Amazon DynamoDB in AWS SDK for Java 2.x can now allow atomic counters, in addition to add customized DynamoDB replace expressions by means of the improved shopper extension framework.

Clients have advised us that they need improved efficiency and consistency when updating desk information. The file replace workflow within the DynamoDB enhanced shopper usually means studying a file from the database to entry present values earlier than writing it again. This overhead may be painful if the information are massive, and it may well incur further prices. Moreover, there’s no assure that the file isn’t up to date between a learn and a write. Which means that it isn’t an atomic set of actions.

This alteration means that you would be able to tag a numeric file attribute as an atomic counter and use DynamoDB to replace the attribute with a particular worth every time that you just name the updateItem operation in your desk. Furthermore, we uncovered an API that fashions replace expressions. This allows you to write your personal extensions that instantly create schema-level replace expressions for DynamoDB.

Ideas

DynamoDB UpdateExpression – That is the syntax utilized by DynamoDB when calling its UpdateItem operation. Use the improved DynamoDB shopper to robotically generate this expression whenever you provide an merchandise to replace.

Enhanced shopper UpdateExpression API – That is the abstraction representing DynamoDB UpdateExpression within the enhanced shopper. Learn extra underneath Introduction to the improved shopper UpdateExpression API later on this publish.

Enhanced shopper extension – This can be a class implementing the DynamoDbEnhancedClientExtension interface that hooks into the logic of operations, comparable to updateItem, and gives the flexibility to switch requests or response parameters.

Extension chain – All the extensions which can be activated for an enhanced shopper. The extensions in a series are utilized so as.

Utilizing Atomic Counters

While you wish to create an atomic counter and replace DynamoDB each time that you just name the updateItem operation, create a desk attribute of the sort Lengthy that represents the counter, and tag it as an atomic counter.

You allow atomic counter performance whenever you instantiate an enhanced shopper. It is because it robotically hundreds the corresponding extension, AtomicCounterExtension. By default, a counter begins at 0 and increments by 1 every time the file within the desk is up to date. You’ll be able to customise it by altering the beginning worth and/or the increment worth, together with destructive values.

The beginning worth is about both when

  • you name the updateItem operation and the attribute doesn’t exist, or
  • you name the putItem operation.

The next instance exhibits create and use atomic counters for each bean-based desk schemas and static desk schemas.

Step 1: Outline a schema with a tagged attribute

Choice 1: Bean-based desk schema
Create an attribute of the sort Lengthy, and annotate it with @DynamoDbAtomicCounter.

@DynamoDbBean
public class Buyer {
  
    @DynamoDbPartitionKey
    public String getId() { ... }
    public void setId(String id) { ... }

    @DynamoDbAtomicCounter
    public Lengthy getUpdateCounter() { ... }
    public void setUpdateCounter(Lengthy counter) { ... }

    @DynamoDbAtomicCounter(delta = 5, startValue = 10)
    public Lengthy getCustomCounter() { ... }
    public void setCustomCounter(Lengthy counter) { ... }
}

Choice 2: Static immutable desk schema
Create a StaticAttribute attribute with the attribute kind Lengthy, and use one of many StaticAttributeTags.atomicCounter() strategies to tag the attribute.

The next code instance assumes that the Buyer.class exists and defines a schema to reference the category.

static last StaticTableSchema<Buyer> TABLE_SCHEMA=
    StaticTableSchema.builder(Buyer.class)
                     .newItemSupplier(Buyer::new)
                     .addAttribute(String.class, a -> a.identify("id") ... )
                     .addAttribute(Lengthy.class, a -> a.identify("defaultCounter")
                                                     .getter(Buyer::getDefaultCounter)
                                                     .setter(Buyer::setDefaultCounter)
                                                     .addTag(atomicCounter()))
                     .addAttribute(Lengthy.class, a -> a.identify("customCounter")
                                                     .getter(Buyer::getCustomCounter)
                                                     .setter(Buyer::setCustomCounter)
                                                     .addTag(atomicCounter(5, 10)))
                     .construct();

Step 2: Create a shopper and desk useful resource

Instantiate the improved DynamoDB shopper, and create the desk useful resource by giving it the beforehand outlined schema:

DynamoDbEnhancedClient enhancedClient = DynamoDbEnhancedClient.create();
TableSchema tableSchema = TableSchema.fromBean(Buyer.class); // or TABLE_SCHEMA for static schema 
DynamoDbTable<Buyer> customerTable = enhancedClient.desk("customers_table", tableSchema);

Step 3: Name DynamoDB

Instantiate a Buyer object and set the important thing attribute. Then, name updateItem and getItem to confirm the automated updates of the counter attributes:

Buyer buyer = Buyer.builder().id("SOME_ID").construct():

customerTable.updateItem(buyer); //each putItem and updateItem can be utilized so as to add information
Buyer retrievedCustomer = customerTable.getItem(buyer)

retrievedCustomer.defaultCounter(); //the worth is 0
retrievedCustomer.customCounter();  //the worth is 10

customerTable.updateItem(buyer); 

retrievedCustomer = customerTable.getItem(buyer)
retrievedCustomer.defaultCounter(); //the worth is 1
retrievedCustomer.customCounter();  //the worth is 15

As you’ll be able to see, we don’t reference the attributes within the file that’s despatched to DynamoDB. Nonetheless, the file is regularly up to date with values every time that you just name the database. Manually setting a worth on the file will trigger DynamoDB to throw a DynamoDBException, saying “Invalid UpdateExpression: Two doc paths overlap […]”, as a result of the replace expression with counters auto-generated by the AtomicCounterExtension will collide with the expression that was created for the file itself.

Making a customized UpdateExpression extension

Write your personal extension that takes benefit of the brand new choice to offer a customized UpdateExpression within the extension framework.

Replace expressions within the extensions are relevant to be used circumstances the place you wish to do the identical factor with an attribute each time that you just name the database, comparable to atomic counters. Nonetheless, in case you want a one-time impact for a single request, then leveraging the extension framework isn’t helpful. Earlier than contemplating assist for single-request replace expressions, we’ll consider the utilization of the brand new UpdateExpression API included on this launch, in addition to the suggestions that we get.

Introduction to the improved shopper UpdateExpression API

An enhanced shopper UpdateExpression consists of a number of UpdateAction that correspond to the DynamoDB UpdateExpression syntax. Earlier than sending an replace request to DynamoDB, the improved shopper parses an UpdateExpression right into a format that DynamoDB understands.

For instance, you’ll be able to create a RemoveAction that can take away the attribute with the identify “attr1” from a file:

RemoveAction removeAction = 
    RemoveAction.builder()
                .path("#attr1_ref")
                .putExpressionName("#attr1_ref", "attr1")
                .construct();

Observe that, whereas utilization of ExpressionNames is elective, we advocate it to keep away from identify collisions.

Step 1: Create an extension class

Create an extension class that implements the beforeWrite extension hook:

public last class CustomExtension implements DynamoDbEnhancedClientExtension {

    @Override
    public WriteModification beforeWrite(DynamoDbExtensionContext.BeforeWrite context) {
        return WriteModification.builder()
                                .updateExpression(createUpdateExpression())
                                .construct();
    }
}

You should use the context object to retrieve details about the next:

  • The reworked merchandise
  • Desk metadata, comparable to customized tags
  • The identify of the operation that’s being invoked

Step 2: Create the UpdateExpression

In our extension, a SetAction modifications the worth of “attr2”, which we are able to infer is a String attribute:

personal static UpdateExpression createUpdateExpression() {
    AttributeValue newValue = AttributeValue.builder().s("A brand new worth").construct();
    SetAction setAction = 
    SetAction.builder()
                .path("#attr1_ref")
                .worth(":new_value")
                .putExpressionName("#attr1_ref", "attr1")
                .putExpressionValue(":new_value", newValue)
                .construct();

    UpdateExpression.builder()
                    .addAction(setAction)
                    .construct();
}

Step 3: Add the extension to a shopper

Add your customized extension to the shopper:

DynamoDbEnhancedClient enhancedClient = DynamoDbEnhancedClient.builder()
                                                              .extensions(new CustomExtension()))
                                                              .construct();

Step 4: Name DynamoDB

Retrieve a reference to the desk and name updateItem:

DynamoDbTable<MyRecord> desk = enhancedClient.desk("tableName", tableSchema);
desk.updateItem(new MyRecord());

Underneath the hood

It may be helpful to grasp how the extension framework and the UpdateItem operation work collectively to mix the output from the extensions with request-level merchandise info to kind a cohesive DynamoDB request:

  • If there are a number of extensions that add UpdateExpressions within the extension chain, then these are merged with none checks to kind a single expression.
  • The merchandise to be up to date that’s supplied by the improved UpdateItemRequest is reworked by the operation to an inside UpdateExpression.
  • The interior UpdateExpression within the UpdateItem operation is merged with the one from the extension framework – if it exists.
  • DynamoDB permits for just one motion to control a single attribute. Due to this fact, any duplicate actions referencing the identical attribute will fail regionally within the shopper when the UpdateExpression is parsed into the low-level DynamoDB request.
  • The improved shopper generates take away statements for any attribute that isn’t explicitly set on an merchandise provided to the UpdateItem operation. As a result of this default conduct (managed by the ignoreNulls flag) interferes with extension performance, the shopper robotically filters these attributes by checking in the event that they’re current in an extension UpdateExpression. If they’re, then the shopper doesn’t create take away statements for them.

Conclusion

On this publish, you’ve realized to make use of atomic counters within the enhanced DynamoDB shopper, and also you’ve seen how extensions can work with the improved UpdateExpression API for customized functions. The improved shopper is open-source and resides in the identical repository because the AWS SDK for Java 2.0.

We hope you discovered this publish helpful, and we stay up for your suggestions. You’ll be able to at all times share your enter on our GitHub points web page, or up-vote different concepts for options that you just wish to see within the DynamoDB enhanced shopper or the AWS SDK for Java basically.

Anna-Karin Salander

Anna-Karin is a maintainer of AWS SDK for Java. She has a ardour for writing maintainable software program and infrastructure, in addition to having fun with gardening, mountain climbing and portray. You will discover her on GitHub @cenedhryn.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments