PrepZone Logo
PrepZone

DynamoDB Streams, DAX, and Transactions

Change data capture, microsecond caching, and multi-item ACID writes.

Why this matters

  • VaultCommerce emits Stream events when order status changes — search index, email, and analytics services react without polling the table.
  • DAX helps read-heavy product-metadata paths but hides stale reads — only cache idempotent, TTL-tolerant data.
  • Checkout must deduct inventory and create an order atomically — TransactWrite with condition checks prevents double-sell.
Partition key: USER#42Routes to shard
Items in partition
ORDER#2026-001Sort key
ORDER#2026-002Sort key
PROFILESort key
Partition key routes to a shard. Sort key orders items within that partition.

DynamoDB Streams

Streams append INSERT, MODIFY, REMOVE records with old/new images. Enable on the table, attach a Lambda event source mapping:

Java
aws dynamodb update-table \
  --table-name VaultCommerce \
  --stream-specification StreamEnabled=true,StreamViewType=NEW_AND_OLD_IMAGES
Java
def handler(event, context):
    for record in event["Records"]:
        if record["eventName"] in ("INSERT", "MODIFY"):
            new_image = record["dynamodb"]["NewImage"]
            if new_image.get("SK", {}).get("S", "").startswith("ORDER#"):
                sync_to_elasticsearch(unmarshall(new_image))

Stream view types

  • KEYS_ONLY — partition and sort keys only.
  • NEW_IMAGE — item after change.
  • OLD_IMAGE — item before change.
  • NEW_AND_OLD_IMAGES — both — VaultCommerce uses this for audit diffs.

Consumers must be idempotent — Lambda retries duplicate events.

DAX caching layer

Java
import amazondax
from boto3.dynamodb.types import TypeDeserializer

client = amazondax.AmazonDaxClient(resource_id="vaultcommerce-dax.xxxxxx.dax-clusters.us-east-1.amazonaws.com:8111")
# Same API as DynamoDB — point SDK at DAX endpoint for reads/writes

DAX maintains item cache with write-through invalidation. VaultCommerce caches PRODUCT# metadata reads through DAX; never cache stock counts that change every checkout.

TransactWriteItems

Java
client.transact_write_items(TransactItems=[
    {
        "Put": {
            "TableName": "VaultCommerce",
            "Item": {"PK": {"S": f"USER#{uid}"}, "SK": {"S": f"ORDER#{oid}"}, ...},
            "ConditionExpression": "attribute_not_exists(PK)",
        }
    },
    {
        "Update": {
            "TableName": "VaultCommerce",
            "Key": {"PK": {"S": f"PRODUCT#{sku}"}, "SK": {"S": "INVENTORY"}},
            "UpdateExpression": "SET qty = qty - :one",
            "ConditionExpression": "qty >= :one",
            "ExpressionAttributeValues": {":one": {"N": "1"}},
        }
    },
])

All items succeed or none apply — conditional failures roll back the whole transaction. Limit: 100 actions, 4 MB payload, items must fit service constraints.

Quick recall

Everything you need if you only revisit this box.

  • Streams: change feed for Lambda — enable NEW_AND_OLD_IMAGES for rich consumers.
  • Stream handlers must be idempotent — at-least-once delivery on retries.
  • DAX: microsecond read cache — write-through; not for rapidly mutating counters.
  • TransactWriteItems: up to 100 all-or-nothing ops with condition expressions.
  • VaultCommerce uses transactions for checkout; Streams for search/email sync.
  • Route inventory reads around DAX; cache stable product metadata through DAX.

Test yourself

Answer these before moving on — recall is what makes it stick.