1

I have the following code to create a dynamoDB table :

def create_mock_dynamo_table():
    conn = boto3.client(
        "dynamodb",
        region_name=REGION,
        aws_access_key_id="ak",
        aws_secret_access_key="sk",
    )
    conn.create_table(
        TableName=DYNAMO_DB_TABLE,
        KeySchema=[
            {'AttributeName': 'PK', 'KeyType': 'HASH'},
            {'AttributeName': 'SK', 'KeyType': 'RANGE'}
        ],
        AttributeDefinitions=[
            {'AttributeName': 'PK', 'AttributeType': 'S'},
            {'AttributeName': 'SK', 'AttributeType': 'S'}],
        ProvisionedThroughput={"ReadCapacityUnits": 5, "WriteCapacityUnits": 5},
    )
    mock_table = boto3.resource('dynamodb', region_name=REGION).Table(DYNAMO_DB_TABLE)
    return mock_table

Then I use it to create two put-items :

    mock_table = create_mock_dynamo_table()
    mock_table.put_item(
        Item={
            'PK': 'did:100000001',
            'SK': 'weekday:monday:start_time:00:30',
        }
    )
    mock_table.put_item(
        Item={
            'PK': 'did:100000001',
            'SK': 'weekday:monday:start_time:00:40',
        },
        ConditionExpression='attribute_not_exists(PK)'
    )

When I do the second put_item, the PK is already there in the system and only the sort key is different. But the condition I am setting only in the existence of same PK. So the second put_item should fail right ?

Sourav Sarkar
  • 406
  • 1
  • 5
  • 14

2 Answers2

3

The condition check for PutItem does not check the condition against arbitrary items. It only checks the condition against an item with the same primary key (hash and sort keys), if such an item exists.

In your case, the value of the sort key is different, so when you put the second item, DynamoDB sees that no item exists with that key, therefore the PK attribute does not exist.

This is also why the condition check fails the second time you run the code—because at that point you do already have an item with the same hash and sort keys.

Matthew Pope
  • 7,212
  • 1
  • 28
  • 49
-1

DynamoDB's "IOPS" is very low and the actual write takes some time. You can read more about it here. But, if you run the code a second time soon after, you'll see that you'll get the expected botocore.errorfactory.ConditionalCheckFailedException.

If I may refer to what I think you're trying to do - mock a DB + data. When you want to mock such an "expensive" resource, make an actual fake class. You'll want to wrap all your DB accesses in the actual code with some kind of dal.py module that consolidates operations such as write/read/etc. Then, you mock those methods/functions.
You don't want to write code so tightly coupled with the chosen DB.

The best practice is using an ORM framework such as SQLAlchemy. It is invaluable to take the time now to learn it. But, you might have time constraints I'm not aware of.

edd
  • 1,307
  • 10
  • 10
  • I think your reasoning about the delay is wrong. First of all I indeed tried it in real dynamoDB, after couple of seconds and it will always work. (I copied the snipped from a notebook). Secondly, I tried it via moto as well. Thirdly, Later I got this question : https://stackoverflow.com/questions/32833351/dynamodb-put-item-if-hash-or-hash-and-range-combination-doesnt-exist So this tells, that conditional put will first search the item with given hash and range. As the hash and range combination is not there in DB, my condition in the second write becomes trivially `true` – Sourav Sarkar May 01 '20 at 09:32
  • @SouravSarkar I stand corrected, then, cheers. I ran a bad test and concluded wrongly although I suspected the key is always treated implicitly in pairs, at first, but just couldn't find any documentation to back it up. – edd May 01 '20 at 13:17