Exam Guide: Developer – Associate
ποΈ Domain 4: Troubleshooting And Optimization
π Task 3: Optimize Applications By Using AWS Services And Features
This task is about making applications faster, cheaper, and more efficient. You need to understand Lambda concurrency and memory tuning, caching at every layer (CloudFront, API Gateway, ElastiCache), messaging optimization with SNS filter policies, and how to read metrics to find bottlenecks. Choose the right optimization for a given symptom such as high latency, throttling, slow stream processing, or runaway cost, and etc.
π Concepts
Lambda Concurrency Types
Type What It Does Cost Use Case Unreserved Shared pool (default 1,000 per region) Pay per invocation Default for most functions Reserved Guarantees capacity AND caps the function No extra cost Protect downstream services, guarantee capacity Provisioned Pre-warms execution environments Pay even when idle Eliminate cold starts for latency-sensitive functionsπ‘ Reserved concurrency does double duty. It guarantees a function gets that many concurrent executions AND prevents it from exceeding that number. Use it to stop a busy function from starving others, or to protect a downstream database from too many connections. Provisioned concurrency is the only thing that eliminates cold starts, but you pay for it 24/7.
The Concurrency Formula
Concurrent executions = (invocations per second) Γ (average duration in seconds)
Enter fullscreen mode Exit fullscreen mode
Example: 100 requests/second Γ 0.5 seconds = 50 concurrent executions needed.
Memory and CPU Relationship
Lambda allocates CPU proportionally to memory. At 1,769 MB you get one full vCPU. More memory means more CPU, which can make a function run faster. Sometimes fast enough that the higher per-ms cost is offset by the shorter duration.
Memory
Relative CPU
Typical Result
128 MB
Fraction of a vCPU
Cheapest per-ms, slowest
512 MB
~0.3 vCPU
Often the sweet spot
1,769 MB
1 full vCPU
Fast, higher per-ms cost
10,240 MB
~6 vCPUs
Fastest, for CPU-bound work
Caching Layers
Layer Service What It Caches TTL Control Edge CloudFront Static content, API responses Cache policies, headers API API Gateway cache Endpoint responses Per-stage, per-method Application ElastiCache (Redis/Memcached) Query results, sessions Set in code Database DAX (DynamoDB only) DynamoDB reads Item + query cache TTLCaching Patterns
Pattern How It Works Best For Cache-aside (lazy loading) Check cache β miss β fetch from DB β store Read-heavy, tolerates some staleness Write-through Write to cache AND DB simultaneously Consistency-sensitive reads Write-behind Write to cache, async write to DB High write throughput TTL-based Entries expire after a set time Most general-purpose cachingπ‘ Cache-aside is the most common pattern. The risk is a cache stampede (many simultaneous misses hitting the DB). Write-through keeps the cache fresh but adds write latency.
CloudFront Cache Keys
The cache key determines what makes a request unique. The fewer components in the key, the higher the cache hit rate.
Cache Key Component Effect on Hit Rate No headers or query strings Highest hit rate Whitelist only what matters Balanced Forward everything Lowest hit rate (every request unique)SNS Subscription Filter Policies
Filter policies let each subscriber receive only the messages it cares about, reducing downstream processing and cost.
Filter Scope Filters On MessageAttributes (default) Message attribute key/value pairs MessageBody Fields inside the message body JSONKey Metrics for Finding Bottlenecks
Metric
Service
What It Signals
Duration
Lambda
Slow function or slow downstream call
IteratorAge
Lambda (Kinesis/DynamoDB)
Consumer falling behind the stream
ApproximateAgeOfOldestMessage
SQS
Consumer too slow, queue backing up
Throttles
Lambda
Hitting concurrency limits
ConsumedReadCapacityUnits
DynamoDB
Hot partition or under-provisioning
CacheHitRate
CloudFront / API Gateway
Cache effectiveness
ποΈ Build A Performance Optimization Lab
Build a Performance Optimization Lab using the AWS Console:
- A Lambda function tuned across memory settings to find the cost/performance sweet spot
- Reserved concurrency configured to protect a downstream service
- API Gateway caching enabled and tested for cache hits
- An SNS topic with subscription filter policies routing messages selectively
- CloudWatch metrics queries to identify bottlenecks
Prerequisites
Part I
Tune Lambda Memory for Cost and Performance
Create a CPU-Bound Function
1 Open the Lambda console β Create function
-
Function name:
MemoryTuningDemo - Runtime: Python 3.13
2 Click Create function
3 Paste this CPU-bound code
import json
import time
def lambda_handler(event, context):
"""
A CPU-bound function that computes prime numbers.
CPU-bound work benefits directly from more memory (= more CPU),
making it ideal for demonstrating memory tuning.
"""
start = time.time()
limit = event.get('limit', 500000)
primes = []
for num in range(2, limit):
is_prime = True
for i in range(2, int(num ** 0.5) + 1):
if num % i == 0:
is_prime = False
break
if is_prime:
primes.append(num)
duration_ms = (time.time() - start) * 1000
return {
'statusCode': 200,
'body': json.dumps({
'primesFound': len(primes),
'durationMs': round(duration_ms, 1),
'memoryConfigured': context.memory_limit_in_mb
})
}
Enter fullscreen mode Exit fullscreen mode
4 Click Deploy
Run the Memory Tuning Experiment
5 Go to Configuration β General configuration β Edit β set Timeout to 1 min 0 sec β Save (so the function doesn’t time out at low memory)
6 Create a test event named PrimeTest with {}
7 Now run the experiment. For each memory setting below, go to Configuration β General configuration β Edit β change Memory β Save, then run the test and record the results.
You’ll see duration drop sharply as memory increases, then flatten out. The point where it flattens is your sweet spot. More memory past that just costs more without speeding things up.
π‘ For CPU-bound work, increasing memory often reduces total cost because the function finishes much faster. The AWS Lambda Power Tuning tool (a Step Functions state machine) automates this experiment and produces a cost/performance graph. Memory and CPU scale together.
Part II
Use Reserved Concurrency to Protect Downstream Services
The Scenario
Imagine MemoryTuningDemo calls a downstream database that can only handle 10 simultaneous connections. If Lambda scales to 100 concurrent executions, it overwhelms the database. Reserved concurrency caps this.
Configure Reserved Concurrency
1 On MemoryTuningDemo, go to Configuration β Concurrency and recursion detection β Concurrency β Edit
2 Select Reserve concurrency
3 Set Reserved concurrency to 10
4 Click Save
π‘ Now this function can never have more than 10 concurrent executions. Any invocations beyond that are throttled (for async sources) or receive a 429 (for synchronous sources).
Observe Throttling
5 Open CloudShell and run a burst of async invocations
for i in $(seq 1 50); do
aws lambda invoke \
--function-name MemoryTuningDemo \
--invocation-type Event \
--payload '{"limit": 1000000}' \
/dev/null &
done
wait
Enter fullscreen mode Exit fullscreen mode
6 Open the CloudWatch console β Metrics β Lambda β By Function Name β MemoryTuningDemo
7 Add the Throttles and ConcurrentExecutions metrics
8 You’ll see ConcurrentExecutions cap at 10 and Throttles climb as excess invocations are rejected
π‘ Reserved concurrency is free and acts as both a guarantee and a ceiling. It’s the answer when a question situation describes a Lambda function overwhelming a downstream resource (RDS connections, a third-party API rate limit). For throttled async invocations, the events are retried. For synchronous, the caller gets a 429.
Part III
Enable and Test API Gateway Caching
Create an API
1 Open the API Gateway console β Create API β REST API β Build
2 API name: CachingDemoAPI β Create API
3. Create resource β Resource name: products β Create resource
4 Select /products β Create method β GET β Lambda Function (proxy) β select MemoryTuningDemo β Create method
Deploy to a Stage
5 Deploy API β New stage β Stage name: prod β Deploy
Enable Caching on the Stage
6 In the left sidebar, click Stages β select prod
7 Under Stage details, click Edit
8 Enable Cache settings:
- Provision API cache: Enabled
- Cache capacity: 0.5 GB
9 Click Save changes (the cache takes a few minutes to provision)
Enable Caching on the Method
10 With the prod stage selected, expand the resource tree and click the GET method under /products
11 Under Method settings, edit:
- Enable method cache: checked
- Cache TTL: 300 seconds
12 Save
Test Cache Behaviour
13 Copy the stage Invoke URL and call the endpoint a few times:
# First call β cache miss (slower, hits Lambda)
curl -w "\nTime: %{time_total}s\n" https://YOUR_API/prod/products
# Subsequent calls β cache hit (faster, served from cache)
curl -w "\nTime: %{time_total}s\n" https://YOUR_API/prod/products
curl -w "\nTime: %{time_total}s\n" https://YOUR_API/prod/products
Enter fullscreen mode Exit fullscreen mode
14 The first call is slower (cache miss β Lambda runs). Subsequent calls within the TTL are much faster (served from cache, Lambda not invoked).
15 Check CloudWatch β API Gateway metrics β CacheHitCount and CacheMissCount
π‘ API Gateway caching is configured per-stage and per-method. The cache key is based on the request parameters you specify. Caching reduces backend load and latency but can serve stale data within the TTL. You can force a cache bypass with the
Cache-Control: max-age=0header (if you enable that option).
Part IV
Optimize Messaging with SNS Filter Policies
Create an SNS Topic
1 Open the SNS console β Topics β Create topic
2 Type: Standard
3 Name: order-events
4 Click Create topic
Create Two SQS Queues
5 Open the SQS console β Create queue
-
Name:
premium-ordersβ Create queue 6 Create another: -
Name:
all-ordersβ Create queue
Subscribe the Queues with Filter Policies
7 Back in SNS β order-events topic β Create subscription
8 For the premium queue:
- Protocol: Amazon SQS
-
Endpoint: select the
premium-ordersqueue ARN - Expand Subscription filter policy
- Filter policy scope: Message attributes
- Policy:
{
"orderType": ["premium"],
"orderTotal": [{"numeric": [">=", 100]}]
}
Enter fullscreen mode Exit fullscreen mode
- Click Create subscription
9 Create another subscription for all-orders with no filter policy (it receives everything)
Test the Filtering
10 On the order-events topic, click Publish message
11 Publish a premium order:
-
Message body:
{"orderId": "ORD-001", "amount": 250} - Under Message attributes, add:
- Name:
orderType, Type: String, Value:premium - Name:
orderTotal, Type: Number, Value:250
- Name:
- Click Publish message
12 Publish a standard order:
-
Message body:
{"orderId": "ORD-002", "amount": 30} - Message attributes:
- Name:
orderType, Type: String, Value:standard - Name:
orderTotal, Type: Number, Value:30
- Name:
- Click Publish message
Verify the Results
13 Open SQS β all-orders β Send and receive messages β Poll for messages
- Both orders appear (no filter)
14 Open SQS β premium-orders β Poll for messages
- Only the premium order ($250) appears. The standard order was filtered out
π‘ Filter policies are evaluated by SNS before delivery, so filtered-out messages never reach the subscriber thus saving SQS, Lambda, and processing costs. Without filter policies, every subscriber receives every message and must filter in code (wasteful). The default scope filters on message attributes; set the scope to MessageBody to filter on body content instead.
Part V
Identify Bottlenecks with CloudWatch Metrics
Common Bottleneck Patterns
Symptom Likely Bottleneck Fix High Lambda Duration Slow downstream call Cache the result, check X-Ray traces High IteratorAge Lambda can’t keep up with the stream Increase parallelization factor, add shards SQSApproximateAgeOfOldestMessage growing
Consumer too slow
Add consumers, increase batch size
DynamoDB throttling
Hot partition or low capacity
Redesign keys, switch to on-demand
API Gateway 429
Throttle limit reached
Raise limits, add caching
High memory usage
Memory leak or large payloads
Profile code, stream large data
Query Lambda Performance with Logs Insights
1 Open CloudWatch β Logs β Logs Insights
2 Select /aws/lambda/MemoryTuningDemo
3 Run this query to spot memory over-provisioning:
filter @type = "REPORT"
| stats avg(@maxMemoryUsed/1000/1000) as avgMemMB,
max(@maxMemoryUsed/1000/1000) as peakMemMB,
max(@memorySize/1000/1000) as configuredMB,
avg(@duration) as avgDurationMs,
pct(@duration, 95) as p95Ms,
pct(@duration, 99) as p99Ms
Enter fullscreen mode Exit fullscreen mode
4 If peakMemMB is far below configuredMB, you’re over-provisioned on memory. If p99Ms is much higher than avgDurationMs, you have tail latency worth investigating with X-Ray.
Using X-Ray to Pinpoint the Slow Call
A trace makes the bottleneck obvious.
API Gateway: 5ms
βββ Lambda: 2500ms
βββ DynamoDB GetItem: 15ms
βββ External API call: 2200ms β bottleneck
βββ SQS SendMessage: 8ms
Enter fullscreen mode Exit fullscreen mode
The external API is the problem. Fixes: cache the response, make the call asynchronous (send to SQS), or add a circuit breaker to fail fast.
π‘ Match the symptom to the metric. “Stream processing is delayed” β IteratorAge. “Queue is backing up” β ApproximateAgeOfOldestMessage. “Which downstream call is slow?” β X-Ray trace.
ποΈ What You Built | π Exam Concepts Recap
What You Built Exam Concept Ran a memory-tuning experiment across settings Memory = CPU. finding the cost/performance sweet spot Set reserved concurrency to 10 and observed throttles Reserved concurrency as guarantee + ceiling Burst-invoked to trigger throttling ConcurrentExecutions and Throttles metrics Enabled API Gateway caching per stage and method Reducing backend load and latency with caching Measured cache hits vs misses with curl timing Cache TTL behavior and CacheHitCount metric Created SNS subscriptions with filter policies Selective message delivery to reduce cost Published messages with matching/non-matching attributes MessageAttributes filter scope Queried@maxMemoryUsed and percentiles in Logs Insights
Detecting over-provisioning and tail latency
Mapped symptoms to metrics (IteratorAge, etc.)
Choosing the right diagnostic for a bottleneck
β οΈ Clean Up Protocol
-
API Gateway β Delete
CachingDemoAPI(this also removes the provisioned cache) -
Lambda β Delete
MemoryTuningDemo -
SNS β Delete the
order-eventstopic (removes subscriptions) -
SQS β Delete
premium-ordersandall-ordersqueues - IAM β Delete the Lambda execution role
- CloudWatch β Delete the log group for the function
Key Takeaways
- Reserved concurrency = guarantee + throttle (free). Provisioned concurrency = no cold starts (paid even when idle).
- Memory = CPU. For CPU-bound work, more memory can lower total cost by finishing faster. Lambda Power Tuning finds the sweet spot.
- Cache-aside is the most common caching pattern. Check cache β miss β fetch β store.
- CloudFront cache keys: fewer components = higher hit rate. Whitelist only what affects the response.
- API Gateway caching is per-stage and per-method, with a configurable TTL.
- SNS filter policies stop unwanted messages before delivery saving downstream cost. Default scope is message attributes.
- IteratorAge = stream consumer falling behind.
- ApproximateAgeOfOldestMessage = SQS queue backing up.
- X-Ray traces are the fastest way to find which downstream call is the bottleneck.
- Match the symptom to the metric.
Additional Resources
- Understanding Lambda function scaling
- Configuring provisioned concurrency for a function
- Cache settings for REST APIs in API Gateway
- Amazon SNS message filtering
- Caching strategies for Memcached
- AWS Lambda Power Tuning
ποΈ
λ΅κΈ λ¨κΈ°κΈ°