TransactWriteItems

Documentation

Groups up to 100 write actions in a single all-or-nothing operation.

Combined size of items in the transaction cannot exceed 4 MB

You cannot target same item with multiple actions within the same transaction

A write transaction can contain multiple item operations across multiple tables. There 4 actions that can be taken:

  • Put initiates a PutItem operation to create a new Item. Condition expression is optional.

  • Update initiates a UpdateItem operation to add/edit/delete attributes on an existing Item. Condition expression is optional.

  • Delete initiates a DeleteItem operation to delete a single item.

item-condition-expression

  • ConditionCheck checks that an item exists or checks the condition of specific attributes of the item. Condition is applied to an item that is NOT getting manipulated by any opration in the transaction. Requires a Condition Expression. Failure of Condition Check fails the entire transaction.

condition-check

Flash Sale Example

An eCommerce vendor publishes a discount coupon/code that is available in limited quantities. E.g., they may release 100 docount code counts - customers can apply it on first-come-first-serve basis. As soon as the count for discount code goes down to 0 it becomes unavailable!! Here is an ERD for the various entities in the mode.

flash-sale-erd

  • Available coupon count maintained in the attribute Remaining
  • A customer can avail a discount code only ONCE
  • When a customer places the order two operations execute in a single unit of work
    • Count for the coupon is decreased by 1
      • Transaction fails if the Remaining is 0
    • An item is created to indicate that Customer has availed the discount code
      • Check if customer has already availed the discount code
aws dynamodb transact-write-items  \
    --transact-items '[ 
        {
                "Update": {
                    "TableName": "FlashSaleDiscounts",
                    "Key": {
                        "PK": {"S":"DISCOUNT#100"}, 
                        "SK": {"S":"DISCOUNT#100"}
                    },
                    "UpdateExpression": "SET #Remaining = #Remaining - :DecreaseBy",
                    "ConditionExpression": "#Remaining > :MinCoupon",
                    "ExpressionAttributeNames": {"#Remaining":"Remaining"},
                    "ExpressionAttributeValues": {":DecreaseBy": {"N":"1"}, ":MinCoupon": {"N":"0"}}
                }
            },
            {
                "Put": {
                    "TableName": "FlashSaleDiscounts",
                    "Item": {
                        "PK": {"S":"CUST#john"}, 
                        "SK": {"S":"DISCOUNT#100"}
                    },
                    "ConditionExpression": "attribute_not_exists(#sk)",
                    "ExpressionAttributeNames": {"#sk":"SK"}
                }
            }
    ]'   \
    --endpoint-url http://localhost:8000

Model import/commit

It is the same file that is available in the project repository !!! transactions/Flash-Sale-Discounts-Model.json

  • Download this file
  • Launch NoSQL workbench
  • Import the file as a new model
  • Commit to Local DynamoDB to try out the transactions
{
  "ModelName": "Flash-Sale-Discounts-Model",
  "ModelMetadata": {
    "Author": "raj@acloudfan.com",
    "DateCreated": "Feb 01, 2023, 05:46 AM",
    "DateLastModified": "Feb 01, 2023, 06:20 AM",
    "Description": "This is part of a course \"DynamoDB for Architects\"",
    "AWSService": "Amazon DynamoDB",
    "Version": "3.0"
  },
  "DataModel": [
    {
      "TableName": "FlashSaleDiscounts",
      "KeyAttributes": {
        "PartitionKey": {
          "AttributeName": "PK",
          "AttributeType": "S"
        },
        "SortKey": {
          "AttributeName": "SK",
          "AttributeType": "S"
        }
      },
      "NonKeyAttributes": [
        {
          "AttributeName": "Remaining",
          "AttributeType": "N"
        },
        {
          "AttributeName": "LoyaltyPoints",
          "AttributeType": "N"
        },
        {
          "AttributeName": "DiscountMinimumLoyaltyPoints",
          "AttributeType": "N"
        }
      ],
      "TableData": [
        {
          "PK": {
            "S": "DISCOUNT#100"
          },
          "SK": {
            "S": "DISCOUNT#100"
          },
          "Remaining": {
            "N": "10"
          }
        },
        {
          "PK": {
            "S": "DISCOUNT#101"
          },
          "SK": {
            "S": "DISCOUNT#101"
          },
          "Remaining": {
            "N": "20"
          }
        },
        {
          "PK": {
            "S": "DISCOUNT#102"
          },
          "SK": {
            "S": "DISCOUNT#102"
          },
          "Remaining": {
            "N": "10"
          }
        },
        {
          "PK": {
            "S": "LOYALTY#5000"
          },
          "SK": {
            "S": "LOYALTY#5000"
          },
          "Remaining": {
            "N": "5"
          },
          "DiscountMinimumLoyaltyPoints": {
            "N": "5000"
          }
        },
        {
          "PK": {
            "S": "LOYALTY#10000"
          },
          "SK": {
            "S": "LOYALTY#10000"
          },
          "Remaining": {
            "N": "5"
          },
          "DiscountMinimumLoyaltyPoints": {
            "N": "10000"
          }
        },
        {
          "PK": {
            "S": "CUST#john"
          },
          "SK": {
            "S": "CUST#john"
          },
          "LoyaltyPoints": {
            "N": "1000"
          }
        },
        {
          "PK": {
            "S": "CUST#anil"
          },
          "SK": {
            "S": "CUST#anil"
          },
          "LoyaltyPoints": {
            "N": "5001"
          }
        },
        {
          "PK": {
            "S": "CUST#jenna"
          },
          "SK": {
            "S": "CUST#jenna"
          },
          "LoyaltyPoints": {
            "N": "19232"
          }
        },
        {
          "PK": {
            "S": "CUST#paul"
          },
          "SK": {
            "S": "CUST#paul"
          },
          "LoyaltyPoints": {
            "N": "131"
          }
        }
      ],
      "DataAccess": {
        "MySql": {}
      },
      "BillingMode": "PROVISIONED",
      "ProvisionedCapacitySettings": {
        "ProvisionedThroughput": {
          "ReadCapacityUnits": 5,
          "WriteCapacityUnits": 5
        },
        "AutoScalingRead": {
          "ScalableTargetRequest": {
            "MinCapacity": 1,
            "MaxCapacity": 10,
            "ServiceRole": "AWSServiceRoleForApplicationAutoScaling_DynamoDBTable"
          },
          "ScalingPolicyConfiguration": {
            "TargetValue": 70
          }
        },
        "AutoScalingWrite": {
          "ScalableTargetRequest": {
            "MinCapacity": 1,
            "MaxCapacity": 10,
            "ServiceRole": "AWSServiceRoleForApplicationAutoScaling_DynamoDBTable"
          },
          "ScalingPolicyConfiguration": {
            "TargetValue": 70
          }
        }
      }
    }
  ]
}