aws / aws-sdk-java-v2

The official AWS SDK for Java - Version 2
Apache License 2.0
2.16k stars 833 forks source link

ConditionalCheckFailedException with version attribute and partition key using auto-generated UUID #5497

Open johnchapin opened 1 month ago

johnchapin commented 1 month ago

Describe the bug

A ConditionalCheckFailedException is thrown when updating an item that has a partition key attribute which uses DynamoDbAutoGeneratedUuid, and a version attribute using DynamoDbVersionAttribute.

If the DynamoDbAutoGeneratedUuid annotation is removed (and the partition key is populated manually) the issue doesn't occur.

Expected Behavior

I expected the item update to succeed.

Current Behavior

A ConditionalCheckFailedException is thrown from the update operation.

Reproduction Steps

Here's the DynamoDbBean class:

@DynamoDbBean
public class Record {

    private String id;
    private String content;
    private Integer version;

    @DynamoDbPartitionKey
    @DynamoDbAutoGeneratedUuid
    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    @DynamoDbVersionAttribute
    public Integer getVersion() {
        return version;
    }

    public void setVersion(Integer version) {
        this.version = version;
    }
}

And here's the test code which produces the exception:

DynamoDbClient client = DynamoDbClient.create();

DynamoDbEnhancedClient enhancedClient = DynamoDbEnhancedClient.builder()
                .extensions(
                        AutoGeneratedUuidExtension.create(),
                        VersionedRecordExtension.builder().build()
                ).build();

DynamoDbTable<Record> table = enhancedClient.table("test_table", TableSchema.fromBean(Record.class));

public void testVersionIncrementedOnUpdate() {
        Record newRecord = new Record();
        newRecord.setContent("foo");
        table.putItem(newRecord);

        // The item is in the table, and has a version of 1
        Record existingRecord = table.scan().items().stream().findFirst().get();
        assertEquals("foo", existingRecord.getContent());
        assertEquals(1, existingRecord.getVersion());

        existingRecord.setContent("bar");

        // Throws ConditionalCheckFailedException
        Record updatedRecord = table.updateItem(existingRecord);

        assertEquals("bar", updatedRecord.getContent());
        assertEquals(2, updatedRecord.getVersion());
}

Possible Solution

No response

Additional Information/Context

Please let me know if this is actually expected behavior, or an unsupported usage of auto-generated UUIDs for partition keys.

AWS Java SDK version used

2.27.1

JDK version used

openjdk 21.0.3 2024-04-16 LTS

Operating System and version

macOS Sonoma 14.5 (23F79)

debora-ito commented 3 weeks ago

@johnchapin I can repro the issue. The problem is every time a DynamoDbAutoGeneratedUuid attribute is updated in the database, a new value is generated. This behavior was probably copied from the v1 DynamoDB Mapper, and v1 Mapper we had a separate annotation for keys - DynamoDBAutoGeneratedKey.

I think the path forward here is to add the equivalent of DynamoDBAutoGeneratedKey in the v2 DynamoDB Enhanced client. What do you think?

johnchapin commented 3 weeks ago

I think the path forward here is to add the equivalent of DynamoDBAutoGeneratedKey in the v2 DynamoDB Enhanced client. What do you think?

@debora-ito That would work for my use case - I discovered the issue when migrating code from the V1 mapper, so an equivalent of DynamoDbAutoGeneratedKey would be ideal.

debora-ito commented 3 weeks ago

Marking this as a feature request.