1.3 Event-Driven Streaming Ingestion with DynamoDB Streams & Lambda
Key Takeaways
- DynamoDB Streams captures time-ordered sequences of item-level modifications (INSERT, MODIFY, REMOVE) with 24-hour strictly ordered retention per partition key, enabling asynchronous Change Data Capture (CDC).
- Kinesis Data Streams for DynamoDB extends retention up to 365 days and supports additional fan-out consumers and Amazon Data Firehose delivery without consuming DynamoDB table read capacity units.
- AWS Lambda Event Source Mapping (ESM) reads streams using polling threads, supporting batch sizes (1 to 10,000 records), batch windowing (up to 300s), parallelization factor (up to 10 concurrent Lambda invocations per shard), and bisect-on-function-error.
- Idempotent processing using unique message identifiers or DynamoDB conditional writes prevents duplicate side-effects caused by Lambda retries during stream processing failures.
1.3 Event-Driven Streaming Ingestion with DynamoDB Streams & Lambda
Change Data Capture (CDC) Architecture
In event-driven cloud architectures, capturing database modifications as a real-time event stream is known as Change Data Capture (CDC). CDC enables downstream decoupled architectures—such as invalidating caches, updating search indexes in OpenSearch, updating data warehouse tables, or triggering serverless workflows—without performing expensive periodic database polling.
AWS provides native CDC capabilities for Amazon DynamoDB through two streaming integrations: DynamoDB Streams and Amazon Kinesis Data Streams for DynamoDB.
DynamoDB Streams Fundamentals
DynamoDB Streams captures a time-ordered sequence of item-level modifications (INSERT, MODIFY, REMOVE) occurring within a DynamoDB table. The log entries are written asynchronously to the stream within milliseconds of the database update.
Key Characteristics of DynamoDB Streams
- Ordering & Scope: Record modification events are strictly ordered per item primary key. Events for distinct items may arrive in arbitrary order.
- Shard Lifecycle: Stream records are organized into shards, which represent a log of table modifications. Shards auto-split and merge in tandem with underlying DynamoDB physical partition scaling.
- 24-Hour Fixed Retention: Stream records are retained for exactly 24 hours. Data older than 24 hours is automatically trimmed and permanently lost. Retention period is non-configurable.
- Zero Impact on Read Capacity: Polling or reading records from DynamoDB Streams does NOT consume table Read Capacity Units (RCUs).
Stream View Types
When enabling DynamoDB Streams, you must specify the Stream View Type, which defines what information is captured in each stream record:
| Stream View Type | Content Included in Event Payload | Use Case |
|---|---|---|
KEYS_ONLY | Only the key attributes of the modified item | Triggering lightweight cache invalidation |
NEW_IMAGE | The entire item as it appears after modification | Forwarding new state to downstream analytical sinks |
OLD_IMAGE | The entire item as it appeared before modification | Auditing deleted records or state cleanups |
NEW_AND_OLD_IMAGES | Both the new and old item state images | Calculating state diffs, operational metrics, or audit logs |
Kinesis Data Streams Integration for DynamoDB
For advanced analytics pipelines, AWS allows streaming DynamoDB table changes directly into Amazon Kinesis Data Streams instead of standard DynamoDB Streams.
Architectural Comparison: DynamoDB Streams vs. Kinesis Data Streams
| Feature / Limit | Native DynamoDB Streams | Kinesis Data Streams for DynamoDB |
|---|---|---|
| Data Retention | Fixed 24 hours (non-extendable) | Up to 365 days (8,760 hours) |
| Max Concurrent Readers | 2 readers per shard max | Up to 20 readers per shard with Enhanced Fan-Out (EFO) |
| Delivery Sinks | Custom consumers (AWS Lambda, KCL) | Direct delivery to S3/Redshift via Amazon Data Firehose |
| Pricing Model | No charge to enable the stream or call its read API; downstream consumers can have separate charges | Pay for Kinesis capacity and payload processing |
| Multi-Region Replication | Used internally by DynamoDB Global Tables | Regional stream; cross-account or cross-Region delivery requires an explicit architecture |
Decision Guidance: Choose native DynamoDB Streams for lightweight, low-cost Lambda triggers requiring <24h retention. Choose Kinesis Data Streams for DynamoDB when requiring retention >24h, fan-out to >2 consumers, or direct ingestion into Amazon S3 via Firehose.
AWS Lambda Event Source Mapping (ESM)
AWS Lambda integrates with streaming sources (DynamoDB Streams, Kinesis, MSK) via an internal infrastructure component called the Event Source Mapping (ESM). The ESM runs a polling loop on behalf of your serverless architecture, fetching batches of records from stream shards and invoking your Lambda function synchronously.
Tuning ESM Batching & Concurrency
- Batch Size (
BatchSize): Configures the maximum number of records (1 to 10,000) passed to a single Lambda invocation. - Maximum Batching Window (
MaximumBatchingWindowInSeconds): Sets the maximum time (0 to 300 seconds) Lambda waits to gather records before invoking the function. IfBatchSizeis not met, Lambda invokes the function as soon as the window expires. - Parallelization Factor (
ParallelizationFactor): By default, Lambda processes each stream shard with 1 concurrent invocation to guarantee ordered processing. SettingParallelizationFactor(from 1 to 10) divides a single shard into concurrent sub-streams grouped by partition key, allowing up to 10 concurrent Lambda invocations per shard while preserving strict partition key ordering!
Resilient Error Handling & Dead Letter Destinations
When a Lambda function throws an unhandled exception while processing a stream batch, the ESM halts shard processing and retries the entire batch repeatedly until records expire (24 hours). This causes a blocked shard (poison pill block).
To prevent shard blocking, configure advanced ESM error controls:
BisectBatchOnFunctionError: When set toTrue, if a batch invocation fails, Lambda automatically splits the failing record batch into two smaller sub-batches and retries them separately. It recursively bisects until the exact failing record is isolated.MaximumRetryAttempts&MaximumRecordAgeInSeconds: Limits how many times Lambda retries a failing record batch or drops records older than a set threshold.- On-Failure Destination (
DestinationOnFailure): Automatically routes metadata about dropped or failed records directly to an Amazon SQS queue or Amazon SNS topic for offline inspection and replay.
Idempotent Stream Processing Patterns
Because stream processing guarantees at-least-once delivery, network retries or Lambda function re-invocations can deliver the same stream event multiple times. Lambda functions MUST be designed to be idempotent (processing the same event multiple times produces the exact same side-effect).
Idempotency Techniques
- Deterministic Unique Keys: Use the DynamoDB Streams
eventIDor business transaction ID as an idempotency key. - DynamoDB Conditional Writes: Maintain a dedicated tracking table in DynamoDB. Before processing, insert the
eventIDusing a conditional expression:attribute_not_exists(event_id)If the insert succeeds, process the payload. If aConditionalCheckFailedExceptionis raised, skip processing as it is a duplicate event.
Code Example: Idempotent Python Lambda Handler for DynamoDB CDC
import json
import os
import time
import boto3
from botocore.exceptions import ClientError
dynamodb = boto3.resource('dynamodb')
PROCESSED_EVENTS_TABLE = os.environ.get('PROCESSED_EVENTS_TABLE', 'ProcessedStreamEvents')
table = dynamodb.Table(PROCESSED_EVENTS_TABLE)
def lambda_handler(event, context):
records = event.get('Records', [])
print(f"Processing batch of {len(records)} DynamoDB stream records.")
for record in records:
event_id = record['eventID']
event_name = record['eventName'] # INSERT, MODIFY, REMOVE
# Attempt idempotent registration
try:
table.put_item(
Item={'event_id': event_id, 'ttl': int(time.time()) + 86400},
ConditionExpression='attribute_not_exists(event_id)'
)
except ClientError as e:
if e.response['Error']['Code'] == 'ConditionalCheckFailedException':
print(f"Duplicate event detected: {event_id}. Skipping execution.")
continue
raise e
# Parse CDC Image Payload
ddb_data = record.get('dynamodb', {})
new_image = ddb_data.get('NewImage', {})
old_image = ddb_data.get('OldImage', {})
print(f"Executing CDC event {event_name} for ID {event_id}")
# Perform downstream integration logic here (e.g. forward to S3/OpenSearch)
return {"statusCode": 200, "body": json.dumps("Stream processing batch completed.")}
An AWS Lambda function processing a DynamoDB Stream batch fails repeatedly due to a single corrupt record ('poison pill'), causing stream processing on that shard to stall indefinitely. How can a data engineer configure the Lambda Event Source Mapping to automatically isolate corrupt records without losing data or blocking shard throughput?
A data architecture requires streaming all table modifications from a high-throughput DynamoDB table directly into Amazon S3 for long-term compliance storage (up to 1 year). The stream must be consumed by 5 separate analytics systems without consuming DynamoDB Read Capacity Units (RCUs). Which configuration meets these requirements?
A data engineering team is building a Change Data Capture (CDC) audit system that requires comparing the exact attribute values of a DynamoDB item BEFORE and AFTER a modification. Which DynamoDB Stream View Type MUST be selected when enabling the stream?