Skip to content

AutoGenerateUUIDExtension Overriding Existing Attribute Values #5703

Open
@Boyapati36

Description

Describe the bug

The AutoGenerateUUIDExtension was incorrectly overriding existing UUID values for items that already contained non-empty values.

This caused some major issues while I working on a project by using DynamoDbTable.updateItem() method. This method didn't update the existing record instead it saves a new record with auto-generated values. When I debuged the code I found the issue is nit with updateItem() method but with the extension. When I tag partition key attribute with tag @DynamoDbAutoGeneratedUuid. I will never be able to update existing record in the Db using updateItem() method.

Regression Issue

  • Select this option if this issue appears to be a regression.

Expected Behavior

When provided an an object of DynamoDbBean.

Expected: when you try to update an existing record on the DB. it updates the record as per the attributes provided by the user operation.

Current Behavior

Current: Instead of updating the existing record, the operation saves a new record onto the DB

Reproduction Steps

DynamoDB Bug Replication Steps: Auto-Generated UUID Overwriting Issue

Prerequisites

  • Spring Boot project with:
    • Lombok library for model annotations.
    • AWS SDK v2.29.1 for DynamoDB.
  • Local DynamoDB instance set up using NoSQL Workbench.

Steps to Reproduce the Issue

1. Create a DynamoDB Entity (Model)

Create a DynamoDB bean using Lombok annotations:

@DynamoDbBean
@NoArgsConstructor
@AllArgsConstructor
public class Customer {
    private String customerId;
    private String customerName;
    private Instant createdDate;

    @DynamoDbAttribute(value = "CustomerId")
    @DynamoDbAutoGeneratedUuid
    @DynamoDbPartitionKey
    @DynamoDbUpdateBehavior(UpdateBehavior.WRITE_IF_NOT_EXISTS)
    public String getCustomerId() {
        return customerId;
    }
    public void setCustomerId(String customerId) {
        this.customerId = customerId;
    }

    @DynamoDbAttribute(value = "CustomerName")
    public String getCustomerName() {
        return customerName;
    }
    public void setCustomerName(String customerName) {
        this.customerName = customerName;
    }

    @DynamoDbAttribute(value = "CreatedDate")
    @DynamoDbAutoGeneratedTimestampAttribute
    @DynamoDbConvertedBy(CstTimeFormatConverter.class)
    @DynamoDbUpdateBehavior(UpdateBehavior.WRITE_IF_NOT_EXISTS)
    public Instant getCreatedDate() {
        return createdDate;
    }
    public void setCreatedDate(Instant createdDate) {
        this.createdDate = createdDate;
    }
}

2. Configure DynamoDB Enhanced Client Bean

Create a DynamoDbEnhancedClient bean in your Spring Boot configuration:

@Configuration
public class DynamoDbConfig {
    @Bean
    public DynamoDbTable<Customer> customerDynamoDbTable(DynamoDbEnhancedClient dynamoDbEnhancedClient){
        return dynamoDbEnhancedClient.table("Customer", TableSchema.fromBean(Customer.class));
    }

    @Bean
    public DynamoDbClient dynamoDbClient() {
        return DynamoDbClient.builder()
                .endpointOverride(java.net.URI.create("http://localhost:8000"))
                .region(Region.US_EAST_1) // Replace with your desired region
                .credentialsProvider(StaticCredentialsProvider.create(
                        AwsBasicCredentials.create("dummy", "vyh65"))) // Use dummy credentials for local
                .build();
    }

    @Bean
    public DynamoDbEnhancedClient dynamoDbEnhancedClient(DynamoDbClient dynamoDbClient) {
        return DynamoDbEnhancedClient.builder()
                .dynamoDbClient(dynamoDbClient)
                .extensions(AutoGeneratedTimestampRecordExtension.create(), AutoGeneratedUuidExtension.create())
                .build();
    }
}

3. Create a DynamoDB Table Bean

Create a DynamoDbTable<MyEntity> using the DynamoDbEnhancedClient:

@Service
public class DynamoDbTableService {
    
    @Autowired
    private DynamoDbEnhancedClient dynamoDbEnhancedClient;

    public DynamoDbTable<MyEntity> getDynamoDbTable() {
        return dynamoDbEnhancedClient.table("MyEntityTable", TableSchema.fromBean(MyEntity.class));
    }
}

4. Create a controller endpoints for table creation and record saving/updating

@RestController
public class ApiController {

    @Autowired
    DynamoDbTable<Customer> customerDynamoDbTable;

    @PostMapping("createRecord")
    public Customer createRecord(@RequestBody Customer customer){
        customer = customerDynamoDbTable.updateItem(customer);

        // Return the updated item
        return customer;
    }

    @PostMapping("createTable")
    public void createTable(){
        customerDynamoDbTable.createTable();
    }

6. Attempt creating table using the above API and then save a record by providing body

{
    "customerName": "XXYYZZ"
}

After record creation you will receive a save records copy as response

{
    "customerId": "623bc3df-579f-4b3d-841e-bed00cfeea0f",
    "customerName": "XXYYZZ", //modify to send as body again
    "createdDate": "2024-11-09T08:09:45.479307200Z"
}

using the modified response as body hit the /createRecord again it should updated the existing record. Instead it creates a new record.

6. Attempt to Update an Existing Record

  1. Create a new MyEntity object, save it using updateItem().
  2. Modify the object's attributes (except for the partition key).
  3. Use updateItem() again to update the record in DynamoDB.

Expected Behavior

  • The updateItem() method should update the existing record with new attribute values.

Actual Behavior

  • Bug: The @DynamoDbAutoGeneratedUuid annotation overrides the existing partition key.
  • The updateItem() method creates a new record instead of updating the existing one.

Summary

The issue is caused by the @DynamoDbAutoGeneratedUuid tag which regenerates the UUID during updateItem() calls, leading to unintended new record creation instead of updating existing entries.

Possible Solution

No response

Additional Information/Context

No response

AWS Java SDK version used

2.29.1

JDK version used

21

Operating System and version

windows 11 23H2

Metadata

Assignees

Labels

bugThis issue is a bug.dynamodb-enhancedp2This is a standard priority issue

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions