Understanding DynamoDB Monitoring
Amazon DynamoDB is a fully managed NoSQL database service, but "fully managed" doesn't mean you can ignore its operational health. Monitoring DynamoDB means continuously observing its performance metrics, resource utilization, and error patterns to ensure your application runs smoothly, costs stay predictable, and users remain happy. The monitoring ecosystem for DynamoDB consists of three pillars: metrics (the raw numerical data about your tables and indexes), alarms (automated alerting when metrics cross dangerous thresholds), and dashboards (visual representations that give you at-a-glance situational awareness).
DynamoDB automatically sends metrics to Amazon CloudWatch at no additional cost. These metrics are the foundation upon which alarms and dashboards are built. Understanding what each metric means, how to interpret it, and when to act on it is the core skill this tutorial will teach you.
Why DynamoDB Monitoring Matters
Without monitoring, DynamoDB operates as a black box. Your application could be experiencing throttling, high latency, or runaway costs without you knowing until customers complain. Effective monitoring delivers several concrete benefits:
- Cost control: DynamoDB bills you based on provisioned capacity (for provisioned tables) or read/write request units consumed (for on-demand tables). Monitoring lets you spot over-provisioning or unexpected usage spikes before they inflate your AWS bill.
- Performance optimization: High
SuccessfulRequestLatencyvalues can indicate hot keys, inefficient query patterns, or the need to switch from provisioned to on-demand capacity mode. - Capacity planning: Throttled requests (the
ThrottledRequestsmetric) signal that your provisioned throughput is insufficient. Monitoring trends over weeks helps you right-size capacity. - Operational incident prevention: Alarms on error metrics like
SystemErrorsorUserErrorscan wake you up before a partial outage becomes a full-blown incident. - Compliance and auditing: Dashboards provide historical evidence of system health for post-mortem reviews and regulatory audits.
Key DynamoDB Metrics Explained
DynamoDB exposes a rich set of CloudWatch metrics. They fall into several logical categories. Let's explore the most important ones that every developer and operator should know.
Capacity Metrics: Provisioned vs. On-Demand
For tables using provisioned capacity, the critical metrics are:
ConsumedReadCapacityUnits– The read capacity units consumed during the period. Compare this againstProvisionedReadCapacityUnitsto gauge utilization.ConsumedWriteCapacityUnits– Same concept for writes.ProvisionedReadCapacityUnitsandProvisionedWriteCapacityUnits– The capacity you've allocated. If consumed regularly approaches provisioned, you risk throttling.
For tables using on-demand capacity, the equivalents are:
ConsumedReadCapacityUnits(still reported)ConsumedWriteCapacityUnits(still reported)
On-demand tables don't have provisioned capacity metrics, but you can still monitor consumed units to understand cost trends, since you pay per request unit.
Throttling and Error Metrics
ThrottledRequests– Requests rejected because they exceed provisioned capacity or the per-partition limit. This is a red-alert metric. Any sustained throttling means lost data or slow user experiences.ReadThrottleEventsandWriteThrottleEvents– More granular throttle counters specifically for read and write operations. Useful for isolating which access pattern is causing throttling.UserErrors– Client-side errors (HTTP 400 series), such as malformed requests, invalid parameters, or attempts to read an item that doesn't exist with certain expectations.SystemErrors– Internal server errors (HTTP 500 series). These are rare and typically indicate an AWS-side problem. An alarm here should trigger immediate investigation, possibly opening an AWS support case.
Latency Metrics
SuccessfulRequestLatency– The average latency of successful requests in milliseconds. Available broken down by operation type:GetRecords,PutRecord,Query,Scan, etc. High latency often correlates with hot partitions or insufficient capacity.ReplicationLatency– For global tables, this measures the lag between writes to the source region and their appearance in replica regions. High replication latency can cause stale reads in multi-region deployments.
Operational and DAX Metrics
ReturnedItemCount– The number of items returned by a query or scan. Monitoring this helps you detect queries that are scanning more data than expected, which wastes read capacity.ReturnedBytes– The volume of data returned. Helps understand data transfer patterns.TimeToLiveDeletedItemCount– Items deleted by TTL expiration. Useful to verify TTL is working and to track cleanup rates.- For DynamoDB Accelerator (DAX): metrics like
CacheHitRate,CacheMissRate,ConnectionCount, andErrorCountare essential.
Setting Up CloudWatch Alarms for DynamoDB
Alarms transform passive metrics into active notifications. An alarm watches a single metric over a time window and triggers when the metric breaches a threshold you define. The alarm can then send an SNS notification, trigger an AWS Lambda function for auto-remediation, or simply appear on a dashboard as "in alarm" state.
Creating a Basic Throttling Alarm via AWS CLI
Suppose you have a provisioned table called Orders and you want to be notified when throttled requests exceed 5 per minute, sustained for 5 consecutive minutes. Here's how to create that alarm using the AWS CLI:
aws cloudwatch put-metric-alarm \
--alarm-name "OrdersTable-Throttling-Alarm" \
--alarm-description "Alarm when Orders table throttles more than 5 requests/min for 5 minutes" \
--namespace "AWS/DynamoDB" \
--metric-name "ThrottledRequests" \
--dimensions Name=TableName,Value=Orders \
--statistic "Sum" \
--period 60 \
--evaluation-periods 5 \
--threshold 5 \
--comparison-operator "GreaterThanOrEqualToThreshold" \
--treat-missing-data "missing" \
--alarm-actions "arn:aws:sns:us-east-1:123456789012:OpsTeamNotification" \
--actions-enabled
Let's break down the key parameters:
--period 60– Evaluate the metric in 60-second windows.--evaluation-periods 5– Require 5 consecutive data points to breach the threshold. This prevents false positives from momentary spikes.--statistic "Sum"– Sum the throttled requests within each period. Using "Sum" makes sense for count-based metrics. For latency, you'd typically use "Average" or "p90" (via extended metrics).--treat-missing-data "missing"– If data is missing (e.g., table idle with zero throttles), CloudWatch doesn't treat it as breaching or not breaching; it maintains the current alarm state. Alternatives includeignore,breaching, ornotBreaching.--alarm-actions– An SNS topic ARN that will receive the alarm notification. You can also specify--ok-actionsfor when the alarm returns to OK state and--insufficient-data-actions.
Creating a High-Latency Alarm for a Specific Operation
For latency, you might want to alarm when the average SuccessfulRequestLatency for Query operations exceeds 100ms over 3 evaluation periods. Notice the additional Operation dimension:
aws cloudwatch put-metric-alarm \
--alarm-name "OrdersTable-QueryLatency-High" \
--alarm-description "Alarm when Query latency exceeds 100ms average for 3 minutes" \
--namespace "AWS/DynamoDB" \
--metric-name "SuccessfulRequestLatency" \
--dimensions Name=TableName,Value=Orders Name=Operation,Value=Query \
--statistic "Average" \
--period 60 \
--evaluation-periods 3 \
--threshold 100 \
--comparison-operator "GreaterThanThreshold" \
--treat-missing-data "notBreaching" \
--alarm-actions "arn:aws:sns:us-east-1:123456789012:OpsTeamNotification"
Here, --treat-missing-data "notBreaching" is safer for latency — if the table isn't receiving queries (and thus has no latency data), we don't want to falsely alarm.
Composite Alarms for Complex Conditions
Sometimes you want to alarm only when multiple conditions are true simultaneously. For example: "Alert if throttling is high AND latency is high." CloudWatch supports composite alarms that combine other metric alarms using boolean logic:
aws cloudwatch put-composite-alarm \
--alarm-name "OrdersTable-Critical-Combined" \
--alarm-description "Critical: both throttling and high latency detected" \
--alarm-rule "ALARM(OrdersTable-Throttling-Alarm) AND ALARM(OrdersTable-QueryLatency-High)" \
--alarm-actions "arn:aws:sns:us-east-1:123456789012:PagerDutyIntegration" \
--actions-enabled
Composite alarms don't have their own metrics, periods, or thresholds — they purely evaluate the state of other alarms. This is powerful for reducing alert noise: you can route individual metric alarms to a low-severity notification channel, and reserve composite alarms for high-severity paging.
Building DynamoDB Dashboards
CloudWatch Dashboards provide a customizable, visual representation of your DynamoDB metrics. A well-designed dashboard lets you spot trends, correlate events, and quickly diagnose issues without sifting through raw metric data.
Creating a Dashboard via the AWS CLI
Below is a complete example that creates a dashboard named DynamoDB-Operations with multiple widgets. The dashboard body is a JSON string containing widget definitions:
aws cloudwatch put-dashboard \
--dashboard-name "DynamoDB-Operations" \
--dashboard-body '{
"widgets": [
{
"type": "metric",
"x": 0,
"y": 0,
"width": 12,
"height": 6,
"properties": {
"view": "timeSeries",
"stacked": false,
"metrics": [
["AWS/DynamoDB", "ConsumedReadCapacityUnits", {"stat": "Sum", "label": "Read RCU Consumed"}],
["AWS/DynamoDB", "ProvisionedReadCapacityUnits", {"stat": "Average", "label": "Read RCU Provisioned"}],
["AWS/DynamoDB", "ConsumedWriteCapacityUnits", {"stat": "Sum", "label": "Write WCU Consumed"}],
["AWS/DynamoDB", "ProvisionedWriteCapacityUnits", {"stat": "Average", "label": "Write WCU Provisioned"}]
],
"region": "us-east-1",
"title": "Capacity: Consumed vs Provisioned",
"period": 300
}
},
{
"type": "metric",
"x": 12,
"y": 0,
"width": 12,
"height": 6,
"properties": {
"view": "timeSeries",
"stacked": false,
"metrics": [
["AWS/DynamoDB", "ThrottledRequests", {"stat": "Sum", "label": "Throttled Requests"}],
["AWS/DynamoDB", "UserErrors", {"stat": "Sum", "label": "User Errors"}],
["AWS/DynamoDB", "SystemErrors", {"stat": "Sum", "label": "System Errors"}]
],
"region": "us-east-1",
"title": "Errors & Throttling",
"period": 60
}
},
{
"type": "metric",
"x": 0,
"y": 6,
"width": 8,
"height": 6,
"properties": {
"view": "timeSeries",
"stacked": false,
"metrics": [
["AWS/DynamoDB", "SuccessfulRequestLatency", "Operation", "GetItem", {"stat": "Average", "label": "GetItem"}],
["AWS/DynamoDB", "SuccessfulRequestLatency", "Operation", "PutItem", {"stat": "Average", "label": "PutItem"}],
["AWS/DynamoDB", "SuccessfulRequestLatency", "Operation", "Query", {"stat": "Average", "label": "Query"}],
["AWS/DynamoDB", "SuccessfulRequestLatency", "Operation", "Scan", {"stat": "Average", "label": "Scan"}]
],
"region": "us-east-1",
"title": "Latency by Operation (ms)",
"period": 300
}
},
{
"type": "metric",
"x": 8,
"y": 6,
"width": 8,
"height": 6,
"properties": {
"view": "singleValue",
"metrics": [
["AWS/DynamoDB", "ConsumedReadCapacityUnits", {"stat": "Sum", "period": 3600, "label": "RCU/Hour"}],
["AWS/DynamoDB", "ConsumedWriteCapacityUnits", {"stat": "Sum", "period": 3600, "label": "WCU/Hour"}]
],
"region": "us-east-1",
"title": "Current Hour Consumption",
"period": 3600
}
},
{
"type": "alarm",
"x": 16,
"y": 6,
"width": 8,
"height": 6,
"properties": {
"title": "Active Alarms",
"alarms": [
"OrdersTable-Throttling-Alarm",
"OrdersTable-QueryLatency-High",
"OrdersTable-Critical-Combined"
]
}
}
]
}'
This dashboard layout uses a 24-column grid. Widgets are positioned by (x, y) coordinates with specified width and height. The widget types include:
- metric – Displays time-series line graphs. The
viewcan betimeSeries,singleValue, orstacked. - alarm – Shows the current state (OK, ALARM, INSUFFICIENT_DATA) of specified alarms, giving an instant health-check panel.
- text – Markdown-enabled text widget for notes, links, or runbook references (not shown above but highly recommended).
Adding a Text Widget with Runbook Information
Including operational context directly on the dashboard reduces mean time to resolution (MTTR) during incidents. Here's how to add a text widget:
{
"type": "text",
"x": 0,
"y": 12,
"width": 24,
"height": 4,
"properties": {
"markdown": "## Runbook Links\n\n- **Throttling Alert:** [Runbook for Orders table throttling](https://wiki.internal/runbook-orders-throttle)\n- **High Latency Alert:** Check hot partition metrics in CloudWatch Insights\n- **On-call:** #ops-alerts on Slack | PagerDuty escalation: @ops-team\n- **Last updated:** 2025-01-15 by platform-eng"
}
}
Working with DynamoDB Metrics Programmatically
Beyond the CLI, you'll often need to interact with DynamoDB metrics from application code — for custom dashboards, automated scaling logic, or integration with third-party monitoring systems. Here are practical code examples using the AWS SDK for Python (boto3).
Fetching Metric Data with boto3
This Python script retrieves the ConsumedReadCapacityUnits for a specific table over the last hour and prints the data points:
import boto3
from datetime import datetime, timedelta
import time
# Initialize CloudWatch client
cloudwatch = boto3.client('cloudwatch', region_name='us-east-1')
# Define the time range
end_time = datetime.utcnow()
start_time = end_time - timedelta(hours=1)
response = cloudwatch.get_metric_statistics(
Namespace='AWS/DynamoDB',
MetricName='ConsumedReadCapacityUnits',
Dimensions=[
{
'Name': 'TableName',
'Value': 'Orders'
}
],
StartTime=start_time,
EndTime=end_time,
Period=300, # 5-minute intervals
Statistics=['Sum', 'Average', 'Maximum'],
Unit='Count'
)
# Sort datapoints chronologically
datapoints = sorted(response['Datapoints'], key=lambda x: x['Timestamp'])
print(f"Retrieved {len(datapoints)} datapoints for the last hour:\n")
for dp in datapoints:
ts = dp['Timestamp'].strftime('%H:%M:%S')
print(f" [{ts}] Sum: {dp['Sum']:.1f} | Avg: {dp['Average']:.1f} | Max: {dp['Maximum']:.1f}")
# Calculate total RCU consumed in the period
total_rcu = sum(dp['Sum'] for dp in datapoints)
print(f"\nTotal RCU consumed in last hour: {total_rcu:.1f}")
Checking Alarm State Programmatically
You might want your application to check alarm states before performing critical operations, or to build a health-check endpoint:
import boto3
cloudwatch = boto3.client('cloudwatch', region_name='us-east-1')
# List all alarms for DynamoDB metrics
response = cloudwatch.describe_alarms(
AlarmNamePrefix='OrdersTable',
StateValue='ALARM', # Only retrieve alarms currently in ALARM state
MaxRecords=50
)
alarming = response.get('MetricAlarms', []) + response.get('CompositeAlarms', [])
if alarming:
print("CRITICAL: The following alarms are in ALARM state:")
for alarm in alarming:
print(f" - {alarm['AlarmName']}: {alarm.get('StateReason', 'No reason available')}")
else:
print("All clear — no OrdersTable alarms are currently firing.")
# Get detailed history for a specific alarm
history = cloudwatch.describe_alarm_history(
AlarmName='OrdersTable-Throttling-Alarm',
HistoryItemType='StateUpdate',
StartDate=datetime.utcnow() - timedelta(days=1),
EndDate=datetime.utcnow(),
MaxRecords=10
)
print("\nRecent state changes for throttling alarm:")
for item in history.get('AlarmHistoryItems', []):
ts = item['Timestamp'].strftime('%Y-%m-%d %H:%M:%S')
summary = item['HistorySummary']
print(f" [{ts}] {summary}")
Automating Alarm Creation Across Multiple Tables
If you manage dozens of DynamoDB tables, manually creating alarms for each is tedious and error-prone. This script iterates through all tables and creates a standardized throttling alarm for each:
import boto3
dynamodb = boto3.client('dynamodb', region_name='us-east-1')
cloudwatch = boto3.client('cloudwatch', region_name='us-east-1')
SNS_TOPIC_ARN = 'arn:aws:sns:us-east-1:123456789012:OpsTeamNotification'
# Fetch all table names
tables = []
paginator = dynamodb.get_paginator('list_tables')
for page in paginator.paginate():
tables.extend(page.get('TableNames', []))
print(f"Found {len(tables)} tables. Creating throttling alarms...")
for table_name in tables:
alarm_name = f"{table_name}-Throttling-Alarm"
try:
cloudwatch.put_metric_alarm(
AlarmName=alarm_name,
AlarmDescription=f"Auto-generated throttling alarm for {table_name}",
Namespace='AWS/DynamoDB',
MetricName='ThrottledRequests',
Dimensions=[{'Name': 'TableName', 'Value': table_name}],
Statistic='Sum',
Period=60,
EvaluationPeriods=5,
Threshold=5,
ComparisonOperator='GreaterThanOrEqualToThreshold',
TreatMissingData='missing',
AlarmActions=[SNS_TOPIC_ARGS],
ActionsEnabled=True
)
print(f" ✓ Created alarm for {table_name}")
except Exception as e:
print(f" ✗ Failed for {table_name}: {str(e)}")
print("Done.")
Infrastructure as Code: CloudFormation Example
For production environments, alarms and dashboards should be defined as infrastructure as code. Here's a CloudFormation snippet that provisions a DynamoDB table along with its monitoring stack:
Resources:
OrdersTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: Orders
BillingMode: PROVISIONED
ProvisionedThroughput:
ReadCapacityUnits: 10
WriteCapacityUnits: 10
AttributeDefinitions:
- AttributeName: orderId
AttributeType: S
KeySchema:
- AttributeName: orderId
KeyType: HASH
OrdersThrottleAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName: Orders-Throttling-Alarm
AlarmDescription: "Alert on throttled requests for Orders table"
Namespace: AWS/DynamoDB
MetricName: ThrottledRequests
Dimensions:
- Name: TableName
Value: !Ref OrdersTable
Statistic: Sum
Period: 60
EvaluationPeriods: 5
Threshold: 5
ComparisonOperator: GreaterThanOrEqualToThreshold
TreatMissingData: missing
AlarmActions:
- !Ref OpsNotificationTopic
OrdersLatencyAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName: Orders-QueryLatency-Alarm
AlarmDescription: "Alert when Query latency exceeds 100ms"
Namespace: AWS/DynamoDB
MetricName: SuccessfulRequestLatency
Dimensions:
- Name: TableName
Value: !Ref OrdersTable
- Name: Operation
Value: Query
Statistic: Average
Period: 60
EvaluationPeriods: 3
Threshold: 100
ComparisonOperator: GreaterThanThreshold
TreatMissingData: notBreaching
AlarmActions:
- !Ref OpsNotificationTopic
OpsNotificationTopic:
Type: AWS::SNS::Topic
Properties:
TopicName: DynamoDB-Ops-Notifications
Subscription:
- Endpoint: ops-team@example.com
Protocol: email
DynamoDBDashboard:
Type: AWS::CloudWatch::Dashboard
Properties:
DashboardName: DynamoDB-Operations
DashboardBody: !Sub |
{
"widgets": [
{
"type": "metric",
"x": 0, "y": 0, "width": 12, "height": 6,
"properties": {
"view": "timeSeries",
"stacked": false,
"metrics": [
["AWS/DynamoDB", "ThrottledRequests", "TableName", "${OrdersTable}", {"stat": "Sum"}],
["AWS/DynamoDB", "UserErrors", "TableName", "${OrdersTable}", {"stat": "Sum"}],
["AWS/DynamoDB", "SystemErrors", "TableName", "${OrdersTable}", {"stat": "Sum"}]
],
"region": "${AWS::Region}",
"title": "Errors & Throttling - Orders Table",
"period": 60
}
},
{
"type": "alarm",
"x": 12, "y": 0, "width": 12, "height": 6,
"properties": {
"title": "Alarm Status",
"alarms": ["${OrdersThrottleAlarm}", "${OrdersLatencyAlarm}"]
}
}
]
}
Using CloudFormation (or Terraform/CDK) ensures your monitoring configuration is version-controlled, repeatable across environments, and won't drift from manual console changes.
Best Practices for DynamoDB Monitoring
Over years of operating DynamoDB at scale, several patterns have emerged that separate effective monitoring from noisy, ignored alerts.
1. Use Multi-Dimensional Alarm Evaluation
Don't alarm on a single datapoint. Always use at least --evaluation-periods 3 (preferably 5) to require sustained breach. Momentary spikes in throttling — such as a brief burst during a deployment — should not page an on-call engineer. Combine this with appropriate --treat-missing-data settings to avoid alarms during quiet periods.
2. Monitor Per-Operation Latency, Not Just Aggregate
The aggregate SuccessfulRequestLatency without the Operation dimension blends all operations together. A slow Scan on an infrequently used secondary index might be hidden by thousands of fast GetItem calls. Always break down latency by operation type (GetItem, PutItem, Query, Scan) to catch hidden performance degradations.
3. Set Alarms on Throttling, Not Just Capacity Utilization
It's tempting to alarm when ConsumedReadCapacityUnits exceeds 80% of provisioned capacity. But DynamoDB's adaptive capacity and burst credits can absorb short spikes above provisioned levels without throttling. Instead, alarm directly on ThrottledRequests — it's the actual user-visible failure signal.
4. Build Tiered Alarm Severities
Create at least three tiers:
- Warning (non-paging): Single metric in ALARM state for 1-2 evaluation periods. Routes to a Slack channel or email for awareness.
- High (paging during business hours): Sustained breach (5+ periods) or composite alarm combining throttle + latency. Routes to PagerDuty with weekday rules.
- Critical (24/7 paging): Composite alarm combining throttle, latency, and system errors. Immediate escalation regardless of time.
5. Dashboard for Humans, Alarms for Machines
Design dashboards to be read by tired engineers at 3 AM. Use clear titles, group related metrics, include text widgets with runbook links, and display alarm states prominently. Use the singleValue widget type for current-hour cost metrics. Avoid cramming too many metrics into one widget — a clean layout saves precious minutes during incidents.
6. Track Cost Metrics Separately
For on-demand tables, create a dashboard panel that sums ConsumedReadCapacityUnits and ConsumedWriteCapacityUnits over hourly or daily windows, then multiply by the per-unit pricing in your head (or use AWS Cost Explorer integration). For provisioned tables, monitor ProvisionedReadCapacityUnits over time — capacity you're paying for but not using is waste.
7. Automate Alarm Creation for All Tables
As your application grows, new tables get added. Use the script shown earlier (or a Lambda function triggered by CloudTrail CreateTable events) to automatically deploy baseline alarms to every new table. A table without alarms is a blind spot waiting to become an outage.
8. Test Your Alarms
Periodically simulate throttling or high latency (e.g., by temporarily lowering provisioned capacity in a dev environment) to verify that alarms fire, notifications reach the right people, and runbook documentation is accurate. An untested alarm chain is a false sense of security.
9. Leverage CloudWatch Metric Math for Derived Insights
CloudWatch supports metric math expressions that compute new time-series from existing metrics. For example, you can calculate the throttling rate as a percentage of total requests:
{
"metrics": [
["AWS/DynamoDB", "ThrottledRequests", {"id": "throttled", "stat": "Sum"}],
["AWS/DynamoDB", "ConsumedReadCapacityUnits", {"id": "reads", "stat": "Sum"}],
["AWS/DynamoDB", "ConsumedWriteCapacityUnits", {"id": "writes", "stat": "Sum"}],
[{"expression": "(throttled / (reads + writes)) * 100", "label": "ThrottleRate%", "id": "rate"}]
],
"view": "timeSeries",
"title": "Throttle Rate as % of Total Ops"
}
This derived metric is often more actionable than raw throttle counts — it normalizes against traffic volume.
10. Use Contributor Insights for Hot Partition Detection
While CloudWatch metrics give aggregate data, DynamoDB Contributor Insights (when enabled) provides per-partition breakdowns. This is invaluable for detecting hot keys — individual partition keys that receive disproportionate traffic and cause throttling despite adequate aggregate capacity. Enable Contributor Insights for production tables and monitor the ContributedThroughput metric with partition dimensions.
Conclusion
Monitoring DynamoDB is not a one-time setup task — it's an ongoing discipline that matures alongside your application. Start with the fundamentals: understand the key metrics, create alarms on throttling and latency, and build a clean dashboard for operational visibility. As your system grows, layer on composite alarms, automated alarm provisioning, metric math, and Contributor Insights. The goal is never to have the most alarms, but to have the right alarms — ones that reliably predict or detect user-impacting issues while minimizing noise. With the patterns and code examples in this tutorial, you have a complete foundation to build a robust, production-grade monitoring system for DynamoDB that keeps your applications fast, your costs visible, and your on-call engineers well-rested.