nflacco
nflacco

Reputation: 5082

Dynamo putItem with multiple conditions not working in localstack

I'm using Dynamo for the first time directly, in toy project locally. I'm trying to create a record guarded by a condition expression- if the username (range key) or uniqueId (hash key) already exist, fail: "attribute_not_exists(UserId) and attribute_not_exists(Username)". However, when I put records with the same usernames, I don't get a conflict- and a scan of the table confirms this. I am using localstack (local AWS analog), if that makes a difference.

Questions:

Here's the logic to create the record:

userID := GenerateUniqueID()
record := UserCredentialsRecord{
    UserID:           userID,
    Username:         username,
    Password:         base64.StdEncoding.EncodeToString(hashedPassword),
    Salt:             base64.StdEncoding.EncodeToString(salt),
    Email:            email,
    AccountCreatedTS: time.Now().Unix(),
}
...

input := &dynamodb.PutItemInput{
    Item:                av,
    TableName:           aws.String(userCredentialsTableName),
    ConditionExpression: aws.String("attribute_not_exists(UserId) and attribute_not_exists(Username)"),
}
...

_, err = session.PutItem(input)
if err != nil {
    fmt.Println("Got error calling PutItem:", err.Error())
}

Here's the Dynamo table in localstack:

localstack         | ++ aws --endpoint-url=http://localstack:4566 dynamodb create-table --table-name UserCredentials --attribute-definitions AttributeName=UserID,AttributeType=S AttributeName=Username,AttributeType=S --key-schema AttributeName=UserID,KeyType=HASH AttributeName=Username,KeyType=RANGE --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1
localstack         | {
localstack         |     "TableDescription": {
localstack         |         "AttributeDefinitions": [
localstack         |             {
localstack         |                 "AttributeName": "UserID",
localstack         |                 "AttributeType": "S"
localstack         |             },
localstack         |             {
localstack         |                 "AttributeName": "Username",
localstack         |                 "AttributeType": "S"
localstack         |             }
localstack         |         ],
localstack         |         "TableName": "UserCredentials",
localstack         |         "KeySchema": [
localstack         |             {
localstack         |                 "AttributeName": "UserID",
localstack         |                 "KeyType": "HASH"
localstack         |             },
localstack         |             {
localstack         |                 "AttributeName": "Username",
localstack         |                 "KeyType": "RANGE"
localstack         |             }
localstack         |         ],
localstack         |         "TableStatus": "ACTIVE",
localstack         |         "CreationDateTime": 1610668286.312,
localstack         |         "ProvisionedThroughput": {
localstack         |             "LastIncreaseDateTime": 0.0,
localstack         |             "LastDecreaseDateTime": 0.0,
localstack         |             "NumberOfDecreasesToday": 0,
localstack         |             "ReadCapacityUnits": 1,
localstack         |             "WriteCapacityUnits": 1
localstack         |         },
localstack         |         "TableSizeBytes": 0,
localstack         |         "ItemCount": 0,
localstack         |         "TableArn": "arn:aws:dynamodb:us-east-1:000000000000:table/UserCredentials"
localstack         |     }
localstack         | }

Upvotes: 0

Views: 762

Answers (2)

Anshul Saraswat Kaul
Anshul Saraswat Kaul

Reputation: 387

In DynamoDB, hash key + range key forms the primary key. Or, only hash key, if we don't have range key defined for our table.

So, what you want can't be done on range-key as that is not a unique.

The point to not here is that - attribute_not_exists doesn't scans the table for you, rather checks the record/item you are working with (which comes from hash-key and range-key in your ddb query).

Upvotes: 1

Mike Dinescu
Mike Dinescu

Reputation: 55760

You have a misconception about what the attribute_not_exists conditional expression does.

It doesn't mean that the attribute does not exist on any item in the table, but rather that the attribute doesn't exist on the item you are working with.

As such, the operation is working as designed.

If you want to make sure that no two items are added to the table with the same username, or user id, you will have to use those two attributes as the primary key for the table, or come up with an alternative method to check for duplicates.

Upvotes: 4

Related Questions