Domain 3 of the DVA-C02 exam requires you to handle complex data operations, not just simple get-and-put actions. This chapter tackles three advanced DynamoDB features — transactions, streams, and DAX — that separate a basic database user from a professional who can build reliable, high-performance systems. Understanding these tools is critical because the real-world exam questions will test your ability to choose the right feature for scenarios like financial payments, order tracking, and read-heavy dashboards.
Jump to a section
A restaurant kitchen is a high-stakes environment where orders must be processed accurately and quickly.
In this kitchen, the head chef (your application) takes an order for a table that requires multiple dishes: a starter, a main course, and a dessert, all for the same group. This is a DynamoDB transaction. The chef cannot put the starter on the pass unless she is certain the main course is cooking and the dessert is chilling. If any single dish fails — the ice cream machine breaks — she sends the whole order back to the waiter, so the table gets nothing rather than a half-baked meal. This is the 'all-or-nothing' guarantee of a transaction.
Now, imagine a clipboard hanging on the wall. Every time a waiter (your application) picks up a finished plate from the pass, he scribbles a note on that clipboard: 'Table 4, steak, 7:32pm'. This clipboard is a DynamoDB Stream. It is a time-ordered list of every change made to the kitchen's database (the menu and stock counts). A junior chef watches this clipboard from a separate station. Whenever she sees a note about 'lamb chops sold out', she immediately updates the chalkboard menu at the front of house. This is a Lambda function triggered by the Stream.
Finally, consider the kitchen's walk-in fridge. Every time a chef needs a specific ingredient, they must walk all the way to the fridge, open the heavy door, check the shelf, and walk back. That is a standard read from the main DynamoDB table. Now, install a small, fast cooler right next to the main cooking station. This cooler holds the most popular ingredients — onions, garlic, butter — that chefs grab dozens of times per shift. This is a DynamoDB Accelerator (DAX) cache. It keeps frequently accessed data lightning-fast and nearby, slashing the time spent on repeated reads.
Let us break down each of these three advanced DynamoDB features as if you were building a system from scratch.
First, transactions. A standard DynamoDB write — like a PutItem operation — changes one item in one table at a time. That works fine for a simple task like updating a single user's profile picture. But what if you need to move money between two bank accounts? You must subtract from Account A and add the same amount to Account B. If your code subtracts from A but the power fails before it adds to B, the money vanishes. A DynamoDB transaction solves this.
A transaction is a collaborative write or read across one or more tables that completes atomically. 'Atomic' means all the changes happen together, or none of them happen at all. DynamoDB offers two transaction APIs: TransactWriteItems and TransactGetItems. TransactWriteItems lets you bundle up to 100 individual write actions (Put, Update, or Delete) into a single all-or-nothing unit. TransactGetItems lets you read up to 100 items from multiple tables in a single consistent call. The exam expects you to know that transactions consume two write capacity units for each write (one for the transaction and one for the actual put) and that they are ideal for scenarios requiring strong consistency across multiple items, like financial ledgers, shopping cart checkouts, and multi-table inventory updates.
Next, DynamoDB Streams. A stream is a time-ordered sequence of item-level changes in a table. Every time a record in a DynamoDB table is created, updated, or deleted, a stream event captures exactly what changed. This stream is not a database; it is a log. It keeps a rolling 24-hour window of changes.
Why does this matter? Without a stream, if you wanted to react to a change in your table — say, sending a welcome email when a new user signs up — you would have to write custom code that polls the table every few seconds looking for new records. That is wasteful, slow, and complicated. A stream pushes the change information to you automatically. You configure a trigger, usually an AWS Lambda function, to read from the stream. Every time a new stream record appears, your function fires and processes the change.
The exam loves to test the ordering guarantee: stream records for a given partition key appear in the order the changes happened. This is crucial because if two users update the same item, you need to process changes in the correct sequence. You also need to know that streams have two view types: KEYS_ONLY (only the key attributes of the changed item), NEW_IMAGE (the entire item after the change), OLD_IMAGE (the entire item before the change), and NEW_AND_OLD_IMAGES (both versions).
Finally, DAX, which stands for DynamoDB Accelerator. DAX is a fully managed, in-memory cache for DynamoDB. Think of it as a super-fast copy of your data sitting in RAM right next to your application. When your application asks for a piece of data, DAX checks its memory first. If the data is there (a cache hit), DAX returns it in microseconds. If the data is not there (a cache miss), DAX fetches it from the underlying DynamoDB table, stores it in memory, and returns it to your application.
DAX is not a separate database. It reads and writes to your DynamoDB table on your behalf. You point your application's SDK client at the DAX cluster endpoint instead of the DynamoDB table endpoint. Your code does not need to change otherwise. DAX handles two main caching strategies: read-through and write-through. For reads, it caches the result of a GetItem or Query request. For writes (PutItem, UpdateItem, DeleteItem), it can write to the DynamoDB table and also update the cache simultaneously, so subsequent reads see the new data immediately.
The exam pushes three key points about DAX. First, it reduces read latency from single-digit milliseconds to microseconds for eventual consistency reads. Second, it is not suitable for strongly consistent reads — you must use the standard DynamoDB table endpoint for those. Third, DAX does not support all DynamoDB operations; it does not work with transactions (TransactGetItems, TransactWriteItems) and has limited support for Scan operations.
Define the Table Schema and Enable Streams
Before you can use transactions, streams, or DAX, you must have a DynamoDB table with a partition key and optionally a sort key. For streams, you must explicitly enable a stream on the table via the AWS Console, CLI, or SDK. You choose the stream view type (e.g., NEW_AND_OLD_IMAGES). Without this step, no stream records are generated.
Write a TransactWriteItems API Call
In your application code, you construct a list of write operations (Put, Update, Delete) and conditional checks, then call the TransactWriteItems API. DynamoDB evaluates all operations. If any operation violates a condition (e.g., stock count goes negative), the entire transaction is cancelled. If all succeed, DynamoDB commits all changes atomically. This replaces the need for manual rollback code.
Create a Lambda Function Triggered by the Stream
In the AWS Console, navigate to the DynamoDB Stream, set up a trigger, and select an existing Lambda function or create a new one. The Lambda function receives a batch of stream records as input. Each record contains the event name (INSERT, MODIFY, REMOVE) and the old/new images based on the stream view type. Your function processes these records — for example, sending an email, updating an external search index, or writing to another database.
Provision and Configure a DAX Cluster
In the AWS Console, create a new DAX cluster. Choose a node type (e.g., dax.r5.large) and the number of nodes. Configure the cluster's VPC, subnet, and security groups so that your application can reach it. DAX automatically creates an endpoint (e.g., mycluster.clustercfg.dax.use1.cache.amazonaws.com:8111). You must update your application's DynamoDB client to point to this DAX endpoint instead of the default DynamoDB endpoint.
Test and Monitor Performance
Run your application through a load test. Monitor the DAX cluster's metrics via CloudWatch, paying attention to 'CacheHitCount', 'CacheMissCount', and 'CPUUtilization'. A healthy cache hit rate is above 90%. If the hit rate is low, adjust the TTL (time-to-live) on cached items, increase the number of nodes, or pre-warm the cache by querying popular items. For streams, verify that your Lambda function processes events correctly by checking the Lambda logs in CloudWatch Logs.
Implement Error Handling for Transactions
Because transactions can fail (due to conditional check failures, throttling, or internal errors), your code must handle TransactionCanceledException. This exception includes a list of cancellation reasons for each action. You should log the specific reason (e.g., 'ConditionalCheckFailed', 'ProvisionedThroughputExceeded') and implement retry logic with exponential backoff for throttling errors. Do not assume transactions always succeed.
A typical IT scenario: you are building the backend for a popular e-commerce mobile app called 'ShopNow'. Your DynamoDB table stores product inventory data: each item has a product ID (partition key), a stock count, a price, and a description. Thousands of users are browsing the app simultaneously. Here is how an IT professional would use these three features in practice.
When a customer adds an item to their basket, the application must check that the item is in stock. But the checkout process involves checking stock across three different items simultaneously. You cannot let the customer pay for an item that goes out of stock mid-checkout. The IT professional uses a TransactWriteItems call. This bundles three Check condition (conditional checks that succeed or fail atomically) and three Update operations: decrement stock for item A, decrement stock for item B, and decrement stock for item C. If any of the items is out of stock, the entire transaction fails, and the customer sees an error message. No money changes hands for a partial order.
Next, when a product's stock count changes (because the transaction succeeded or because a warehouse restocked), the IT professional needs to update a real-time 'Popular Products' widget on the mobile app's home screen. They enable a DynamoDB Stream on the Products table. The stream captures every change. A Lambda function is subscribed to that stream. The function reads the stream event, extracts the new stock count, and pushes a message to a WebSocket connection, which the mobile app uses to update the widget instantly. Without the stream, the developer would have to write a scheduled job that runs every minute and queries all products — wasteful and slow.
Finally, the ShopNow app has a product detail page that shows pricing, descriptions, and reviews. This page is read thousands of times per second. Hitting the main DynamoDB table for every single request would be expensive and slow due to the overhead of reading from disk. The IT professional deploys a DAX cluster. They configure their application's SDK to connect to the DAX endpoint instead of the DynamoDB endpoint. When a user views a product, the SDK asks DAX first. If other users viewed the same product a moment ago, DAX already has the data in its RAM. The response comes back in microseconds instead of milliseconds. This massively reduces the read capacity units consumed from the main table, lowering AWS costs and improving the user experience.
The critical action the IT professional takes is monitoring: they set up CloudWatch alarms on the DAX cluster's CPU utilisation and cache hit ratio. If the cache hit ratio drops below 85%, they know the cache is not warming properly, and they might need to increase the cluster's node size or adjust the TTL (time-to-live) settings on cached items.
The DVA-C02 exam tests these three features in very specific ways. You will see scenario-based questions that describe a business requirement and ask you to pick the correct DynamoDB feature. The examiners are less interested in you memorising syntax and far more interested in you understanding trade-offs and constraints.
First, transactions. The exam will describe a situation where you need to ensure that multiple writes across one or more tables either all succeed or all fail, with no partial state. The correct answer will be 'Use a DynamoDB transaction (TransactWriteItems)'. The trap answers will suggest using a series of individual PutItem calls with conditional expressions, or using a combination of PutItem and a scheduled Lambda to clean up failures. Do not fall for the cleanup pattern — it breaks the atomicity guarantee. Another key trap: the exam may mention a scenario where you need to guarantee that a read sees the absolute latest data across multiple tables. The answer is not DynamoDB transactions for reads (TransactGetItems is for transactional reads, but they are not strongly consistent across tables by default). You must know that TransactGetItems provides serialisability, not global consistency.
Second, DynamoDB Streams. The exam loves to test the idea of change data capture (CDC). A typical question: 'An application needs to send a notification every time a record in a DynamoDB table is updated. What is the most efficient solution?' The correct answer is 'Create a DynamoDB Stream and subscribe an AWS Lambda function to it.' The trap answers include:
Use a scheduled CloudWatch event to run a Lambda that polls the table every minute.
Use a DynamoDB TTL to trigger a Lambda.
Use an SQS queue and have the application push a message after each update (this creates coupling and complexity).
You must also memorise the four image types (KEYS_ONLY, NEW_IMAGE, OLD_IMAGE, NEW_AND_OLD_IMAGES) because the exam will test which one to use. For instance: 'You need to track only the previous and current state of a record's attributes after an update.' Answer: NEW_AND_OLD_IMAGES.
Finally, DAX. The exam will ask you to reduce read latency for frequently accessed data. The correct answer is 'Use DynamoDB Accelerator (DAX)'. The traps include:
Use Amazon ElastiCache with Redis or Memcached (this is a valid option but requires manual cache management and is less integrated).
Use the DynamoDB ReadOnly replica in another Region (this is for geographic distribution, not latency reduction for a single application).
Use strongly consistent reads (these are slower, not faster).
A critical exam point: DAX does not support strongly consistent reads. If the question says 'Your application needs to read the absolute latest data every time', the answer is not DAX — it is a standard DynamoDB GetItem with ConsistentRead=true. DAX only serves eventually consistent reads. Another common trap: DAX does not support transactions. If a question combines the need for fast reads and atomic writes, you must explain that DAX provides the fast reads, but the writes still go through the transaction API directly to the DynamoDB table.
DynamoDB transactions provide atomic all-or-nothing writes across up to 100 actions in one or more tables, costing double the write capacity units.
DynamoDB Streams capture a time-ordered sequence of item-level changes in a table and retain records for a maximum of 24 hours.
DAX is an in-memory cache that reduces eventual consistent read latency from milliseconds to microseconds but does not support strongly consistent reads or transactions.
You must enable DynamoDB Streams at the table level and specify a view type (KEYS_ONLY, NEW_IMAGE, OLD_IMAGE, NEW_AND_OLD_IMAGES) to control what data appears in stream records.
DAX requires a dedicated cluster of nodes, and you point your application's SDK client at the DAX endpoint, not the DynamoDB table endpoint, to leverage caching.
When building a multi-step financial or inventory process, use TransactWriteItems instead of separate PutItem calls to prevent partial updates and data corruption.
These come up on the exam all the time. Here's how to tell them apart.
DynamoDB Transactions
Guarantees atomicity across multiple items and tables
Consumes 2 write capacity units per write action
Suitable for multi-step financial or inventory operations
Individual PutItem Calls with Conditions
Only checks conditions on a single item at a time
Consumes 1 write capacity unit per write
Risk of partial updates if subsequent writes fail
DynamoDB Streams
Automatically captures every change to a table with no application changes
Records are available for 24 hours in a defined order (per partition key)
Ideal for change data capture (CDC) and event-driven architectures
Amazon SQS as a Change Queue
Requires manual push of messages from the application code
No guaranteed ordering unless you use a FIFO queue
Adds coupling between the application and the message queue
DAX (DynamoDB Accelerator)
Fully managed, tightly integrated with DynamoDB SDK
Supports read-through and write-through caching automatically
Only works with eventually consistent reads and limited operations
Amazon ElastiCache (Redis/Memcached)
Requires manual code to manage cache hydration and invalidation
More flexible — supports any data structure (Redis) or simple key-value (Memcached)
Works with any backend, not just DynamoDB
Strongly Consistent Reads (DynamoDB)
Returns the most up-to-date data, but slower (milliseconds)
Consumes more read capacity units (1 vs 0.5)
Cannot be cached by DAX; must query the table directly
Eventually Consistent Reads (DynamoDB via DAX)
Returns data that may be slightly stale (up to 1 second old)
Much faster when served from DAX cache (microseconds)
Consumes fewer read capacity units per read
Mistake
Transactions in DynamoDB are the same as SQL database transactions and support rollbacks to a save point.
Correct
DynamoDB transactions are atomic (all-or-nothing) but do not support user-defined rollback to a save point. If the transaction fails, DynamoDB cancels the entire thing — you cannot partially commit and then roll back individual actions.
Beginners with relational database experience assume DynamoDB transactions work like SQL transactions with BEGIN, COMMIT, and ROLLBACK commands. DynamoDB transactions are simpler but more rigid.
Mistake
DynamoDB Streams store the full history of changes forever, like a database of all past states.
Correct
DynamoDB Streams only keep a rolling 24-hour window of changes. After 24 hours, the stream records are automatically deleted. They are not a permanent audit log.
The word 'stream' sounds permanent and continuous, leading people to think it is an infinite log. They confuse it with services like AWS CloudTrail or Amazon S3, which can store records indefinitely.
Mistake
DAX is a separate database that you can write to independently, and it synchronises back to DynamoDB automatically.
Correct
DAX is a cache, not a database. You cannot write directly to it; your application writes to the DAX cluster, which then writes to the DynamoDB table. It is an accelerator in front of DynamoDB, not an independent store.
The term 'Accelerator' and the fact that you connect to a separate endpoint make beginners assume it is a separate service like Amazon RDS or ElastiCache. They do not realise the underlying table remains the source of truth.
Mistake
You can use DAX with any type of DynamoDB operation, including transactions, Scan, and strongly consistent reads.
Correct
DAX has significant limitations: it does not support transactions (TransactWriteItems, TransactGetItems), it does not support strongly consistent reads, and it has limited support for Scan operations (only paginated scans work).
Beginners assume DAX is a transparent upgrade that handles everything faster. They do not read the documentation carefully and miss the operational constraints.
Mistake
If you use DynamoDB Streams, you must write code to poll the stream every few seconds to get new changes.
Correct
DynamoDB Streams work with AWS Lambda triggers and the Kinesis Client Library (KCL) for automatic polling. You do not write polling code; AWS handles the polling for you. You write the code that processes each change event.
Beginners with a background in traditional databases are used to writing polling loops. They do not understand the event-driven, push-based model of Lambda triggers.
Reveal each answer, then mark whether you got it right. Score 60%+ to unlock the next chapter.
Conditional expressions on individual writes only check conditions for that single item. Transactions bundle multiple writes and conditions across items and tables, guaranteeing atomicity: either all conditions pass and all writes succeed, or none do.
DynamoDB Streams read capacity is separate from your table's read capacity. Writing to a table with streams enabled consumes the same write capacity as usual, plus a small additional overhead for writing to the stream. AWS charges separately for stream reads from the stream itself.
No. DAX only caches eventually consistent reads. If your application requires strongly consistent reads, you must bypass DAX and query the underlying DynamoDB table directly with the ConsistentRead parameter set to true.
Stream records are retained for a maximum of 24 hours. After that, they are automatically deleted. This is not configurable. If you need long-term storage of change history, you must process the stream and store the records in another service like S3 or a log database.
DynamoDB transactions only operate within a single AWS Region. They cannot span multiple Regions. For cross-Region consistency, you must use other patterns like application-level consensus or eventual replication with conflict resolution.
DAX clusters are deployed across multiple Availability Zones. If a node fails, the cluster automatically replaces the node with a new one. However, the cache contents are lost. The new node will start with an empty cache and warm up as requests come in, temporarily increasing latency and DynamoDB table read consumption.
You've just covered DynamoDB Advanced: Transactions, Streams, and DAX — now see how well it sticks with free DVA-C02 practice questions. Full explanations included, no account needed.
Done with this chapter?