1

Say, I want to save/create new item to the DynamoDb table, if and only if there is not any existent item already that that would contain the referenceId equal to the same value I set.

In my case I want to create a item with withReferenceId=123 if there is not any other withReferenceId=123 in the table.

the referenceId is not primary key! (I don not want it to be it)

So the code:

 val withReferenceIdValue = "123";
 val saveExpression = new DynamoDBSaveExpression();

    final Map<String, ExpectedAttributeValue> expectedNoReferenceIdFound = new HashMap();
    expectedNoReferenceIdFound.put(
            "referenceId",
            new ExpectedAttributeValue(new AttributeValue().withS(withReferenceIdValue)).withComparisonOperator(ComparisonOperator.NE)
    );
    saveExpression.setExpected(expectedNoReferenceIdFound);

    newItemRecord.setReferenceId(withReferenceId);

    this.mapper.save(newItemRecord, saveExpression); // do not fail..

That seems does not work.

I the table has the referenceId=123 already the save() does not fail. I expected this.mapper.save to fail with exception.

Q: How to make it fail on condition?

I also checked this one where they suggest to add auxiliary table (transaction-state table)..because seems the saveExpression works only for primary/partition key... if so:

not sure why there that limitation. in any case if it is primary key one can not create duplicated item with the same primary key.. why creating conditions on first place. 3rd table is too much.. why there is not just NE to whatever field I want to use. I may create an index for this filed. not being limited to use only primary key.. that what I mean

UPDATE:

My table mapping code:

@Data // I use [lombok][2] and it does generate getters and setters.
@DynamoDBTable(tableName = "MyTable")
public class MyTable {

    @DynamoDBHashKey(attributeName = "myTableID")
    @DynamoDBAutoGeneratedKey
    private String myTableID;
    @DynamoDBAttribute(attributeName = "referenceId")
    private String referenceId;
    @DynamoDBAttribute(attributeName = "startTime")
    private String startTime;
    @DynamoDBAttribute(attributeName = "endTime")
    private String endTime;
    ...
}
ses
  • 13,174
  • 31
  • 123
  • 226
  • Is `referenceId` a string in your DB? – Thiyagu Apr 24 '18 at 15:31
  • Does `referenceId` name match the column name? – Thiyagu Apr 24 '18 at 15:57
  • It should work for non-hash/range keys as well. I couldn't find any problem with your code. – Thiyagu Apr 24 '18 at 17:00
  • If you don't mind can you show your `DynamoDBTable` POJO model that has referenceId field/getters/setters – Thiyagu Apr 24 '18 at 17:02
  • Try moving the `@DynamoDBAttribute` to the getters – Thiyagu Apr 24 '18 at 18:18
  • As a last step, go to the DynamoDB console and see the column name. I *guess* it would be "ReferenceId" and not "referenceId" (or you may have both) – Thiyagu Apr 24 '18 at 18:46
  • copied from the console: "referenceId". there is only one. I redeployed the stack. and I can see in this table new duplicated referenceId=123 created. with no expected exception. – ses Apr 24 '18 at 18:49

1 Answers1

1

Correct me if I'm wrong, but from the:

https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/dynamodb-dg.pdf

Conditional Writes By default, the DynamoDB write operations (PutItem, UpdateItem, DeleteItem) are unconditional: each of these operations will overwrite an existing item that has the specified primary key

the primary key - that makes me thing that the conditional write works ONLY with primary keys

--

Also there is attempt use the transactional way r/w from the db. There is a library. That event has not maven repo: https://github.com/awslabs/dynamodb-transactions

As an alternative seems is the way to use 3rd transaction table with the primary keys that are responsible to tell you whether you are ok to read or write to the table. (ugly) as we replied here: DynamoDBMapper save item only if unique

Another alternative, I guess (by design): it is to design your tables in a way so you use the primary key as your business-key, so you can use it for the conditional writes.

--

Another option: use Aurora :)

--

Another options (investigating): https://aws.amazon.com/blogs/database/building-distributed-locks-with-the-dynamodb-lock-client/ - this I do not like either. because potentially it would create timeouts for others who would want to create new items in this table.

--

Another option: Live with this let duplication happens for the item-creation (not including the primary key). And take care of it as a part of "garbage collection". Depends on the scenario.

ses
  • 13,174
  • 31
  • 123
  • 226
  • So, were you using different primary keys with same referenceId so far? – Thiyagu Apr 24 '18 at 19:36
  • `saveExpression` works for all fields. I've checked it for one of my tables and it works fine. If the second record you insert has a different/new hashKey, then checking on referenceId won't work – Thiyagu Apr 24 '18 at 19:39
  • If you mean no two record can have same referenceId, then it must be a key – Thiyagu Apr 24 '18 at 19:40
  • yes, i mean: yes I mean no two should not have same referenceId (and by design I can not make it to be the primary key, because I already have one in this table) - that what I tried to prevent with the saveExpression – ses Apr 24 '18 at 20:01