One order can contain one or many products. Also an order belongs to a customer. The order, product and customer are our item models in DB. A new order creation accepts a request like below.
{
"customer_id": "c8fb0e64-43b0-45b1-a9e7-fe2ce61c2c1f",
...
"products": [
{
"id": "7362ea92-2063-48ad-9bfe-c91a5d9af4a6",
...
},
{
"id": "83e38b2f-2839-43c5-a834-0834d841e566",
...
}
]
}
When I create a new order, I use TransactWriteItems
operation which works fine so far (see the image below). However, at the moment I am not checking the existence of:
c8fb0e64-43b0-45b1-a9e7-fe2ce61c2c1f
).7362ea92-2063-48ad-9bfe-c91a5d9af4a6
and 83e38b2f-2839-43c5-a834-0834d841e566
)when putting the items in DB.
As you can see in the image, customer (the one at the bottom) and product items (two on top) are already in same table. A customer is identifiable by part_key
and/or customer_id
attributes while a product is identifiable by part_key
and/or product_id
attributes. Also each items in table has a _type
attribute to express what items they are.
The question is, how do we in general manage existence check before inserting a new item in DB with transactions? I am aware of attribute_exists
and conditional checks but not sure about the right approach as far as the performance and cost considerations go.
Here's is my code for the reference purposes if it helps.
func (d DynamoDB) CreateOrder(ctx context.Context, model database.Order) error {
items := make([]types.TransactWriteItem, 0, len(model.Products)+1)
for _, product := range model.Products {
product.Key = database.Key{
PartKey: "order#" + product.OrderID,
SortKey: "product#" + product.ID,
}
product.Type = "order_product"
item, err := attributevalue.MarshalMap(product)
if err != nil {
return err
}
items = append(items, types.TransactWriteItem{
Put: &types.Put{
TableName: aws.String(d.Table),
Item: item,
},
})
}
model.Key = database.Key{
PartKey: "order#" + model.ID,
SortKey: "order#" + model.ID,
}
model.Type = "order"
item, err := attributevalue.MarshalMap(model)
if err != nil {
return err
}
items = append(items, types.TransactWriteItem{
Put: &types.Put{
TableName: aws.String(d.Table),
Item: item,
},
})
_, err = d.Client.TransactWriteItems(ctx, &dynamodb.TransactWriteItemsInput{
TransactItems: items,
})
if err != nil {
return err
}
return nil
}
attribute_not_exists () with condition expression can be used to check for item existence. There are no extra costs to adding condition expressions. However, even if your condition fails you will be charged for the write operation (put/transact) in the same manner as it would have cost in case of a successful write.
Another way to to avoid this extra cost is to simply do a get request to check for existence as the read would cost less. Dynamodb WCU-RCU