Cosmos DB Performance and Operations¶
Table of Contents¶
- Request Units and Throughput
- Query Optimization
- Indexing Strategies
- Consistency Levels
- Change Feed
- Monitoring and Diagnostics
- Backup and Disaster Recovery
- Exam Tips
Request Units and Throughput¶
Understanding Request Units (RUs)¶
What is a Request Unit?¶
- Abstract measure of database operations
- Combines CPU, memory, and IOPS
- 1 RU = Read 1 KB item by ID and partition key
- Deterministic and predictable
RU Consumption Examples¶
| Operation | Approximate RU Cost |
|---|---|
| Point read (1 KB) | 1 RU |
| Point read (100 KB) | 10 RU |
| Query (1 KB result) | 2.3+ RU |
| Insert (1 KB) | 5-6 RU |
| Update (1 KB) | 5-10 RU |
| Delete (1 KB) | 5 RU |
| Stored procedure | Varies by logic |
Factors Affecting RU Consumption¶
1. Item Size¶
- Larger items = more RUs
- Linear relationship
- Reading 10 KB β 10 RUs (point read)
2. Item Properties¶
- More properties = more indexing overhead
- Nested properties cost more
- Arrays and objects increase cost
3. Indexing¶
- More indexes = higher write cost
- Excluding properties reduces cost
- Indexing policy impacts all operations
4. Consistency Level¶
| Consistency | Read RU Cost | Write RU Cost |
|---|---|---|
| Strong | 2x | 1x |
| Bounded Staleness | 2x | 1x |
| Session | 1x | 1x |
| Consistent Prefix | 1x | 1x |
| Eventual | 0.5x | 1x |
5. Query Complexity¶
- Filters and predicates
- Aggregations (COUNT, SUM, AVG)
- ORDER BY, GROUP BY
- JOINs within document
- User-defined functions (UDFs)
Throughput Models¶
Provisioned Throughput (Manual)¶
// Database-level (shared across containers)
await client.CreateDatabaseAsync(
new DatabaseProperties { Id = "myDatabase" },
throughput: 10000
);
// Container-level (dedicated)
await database.CreateContainerAsync(
new ContainerProperties("myContainer", "/partitionKey"),
throughput: 1000
);
Characteristics: - Fixed RU/s allocation - Reserved capacity - Predictable cost - Can scale up/down programmatically
Cost: $0.008 per 100 RU/s per hour (varies by region)
Autoscale Throughput¶
await database.CreateContainerAsync(
new ContainerProperties("myContainer", "/partitionKey"),
ThroughputProperties.CreateAutoscaleThroughput(maxRU: 10000)
);
Characteristics: - Scales from 0.1x to max (10% to 100%) - Automatic scaling based on usage - Scales up instantly, down gradually - Pay for actual peak usage per hour
When to Use: - Variable workloads - Unpredictable traffic - Spiky patterns - Development/test environments
Cost Calculation: - Minimum: 10% of max RU/s - Charged hourly for highest RU/s used - Example: Max 10K RU/s, used 5K β charged for 5K
Serverless¶
// Account-level setting (serverless account)
// No throughput provisioning needed
await database.CreateContainerAsync(
new ContainerProperties("myContainer", "/partitionKey")
);
Characteristics: - Pay-per-request - No minimum throughput - Auto-scales to workload - Container limits: 50 GB storage, 5K RU/s
When to Use: - Development and testing - Infrequent access patterns - Small workloads - Unpredictable traffic (small scale)
Cost: Per million RU consumed + storage
Burst Capacity¶
Temporary ability to exceed provisioned throughput.
Features: - Up to 300 seconds of burst - System provides additional RUs - No additional cost - Best-effort basis (not guaranteed)
Use Cases: - Occasional spikes - Batch processing - Data migration - Temporary load
Rate Limiting (429 Errors)¶
Causes¶
- Exceeded provisioned RU/s
- Hot partition (>10K RU/s)
- Burst capacity exhausted
Handling 429s¶
var response = await container.CreateItemAsync(item);
if (response.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
{
// Wait for retry-after duration
await Task.Delay(response.Headers.RetryAfter);
// Retry operation
}
// Or use SDK's built-in retry
var options = new CosmosClientOptions
{
MaxRetryAttemptsOnRateLimitedRequests = 9,
MaxRetryWaitTimeOnRateLimitedRequests = TimeSpan.FromSeconds(30)
};
Query Optimization¶
Query Performance Fundamentals¶
Point Reads (Best Performance)¶
// 1 RU for 1 KB item
var response = await container.ReadItemAsync<Item>(
id: "item-123",
partitionKey: new PartitionKey("partition-value")
);
Characteristics: - Lowest RU cost - Predictable latency (<10ms) - Requires ID and partition key
Single-Partition Queries¶
-- Executes within one partition
SELECT * FROM c
WHERE c.partitionKey = 'partition-value'
AND c.status = 'active'
Characteristics: - Lower RU cost than cross-partition - Faster execution - Scales linearly
Cross-Partition Queries¶
-- Fan-out across all partitions
SELECT * FROM c
WHERE c.status = 'active'
Characteristics: - Higher RU cost - Higher latency - Parallel execution - Use sparingly
Query Optimization Techniques¶
1. Use Partition Key in Filters¶
-- Good: Single partition
SELECT * FROM c
WHERE c.partitionKey = 'value'
AND c.status = 'active'
-- Avoid: Cross-partition
SELECT * FROM c
WHERE c.status = 'active'
2. Use Composite Indexes¶
// Indexing policy
{
"compositeIndexes": [
[
{"path": "/category", "order": "ascending"},
{"path": "/price", "order": "descending"}
]
]
}
-- Efficient with composite index
SELECT * FROM c
WHERE c.category = 'electronics'
ORDER BY c.price DESC
3. Avoid SELECT *¶
-- Bad: Returns all properties
SELECT * FROM c WHERE c.partitionKey = 'value'
-- Good: Select only needed properties
SELECT c.id, c.name, c.price
FROM c
WHERE c.partitionKey = 'value'
4. Use EXISTS for Filtering¶
-- Check for array element existence
SELECT * FROM c
WHERE EXISTS(
SELECT VALUE tag
FROM tag IN c.tags
WHERE tag = 'featured'
)
5. Limit Results¶
-- Use TOP for pagination
SELECT TOP 100 * FROM c
WHERE c.category = 'electronics'
ORDER BY c.price
-- Or OFFSET/LIMIT
SELECT * FROM c
ORDER BY c.price
OFFSET 100 LIMIT 100
Pagination¶
Continuation Tokens¶
var query = container.GetItemQueryIterator<Item>(
"SELECT * FROM c WHERE c.category = 'electronics'",
requestOptions: new QueryRequestOptions
{
MaxItemCount = 100,
PartitionKey = new PartitionKey("electronics")
}
);
while (query.HasMoreResults)
{
var response = await query.ReadNextAsync();
foreach (var item in response)
{
// Process item
}
// response.ContinuationToken for next page
}
Best Practices: - Store continuation token client-side - Set appropriate MaxItemCount - Don't rely on specific page sizes
Query Metrics¶
var query = container.GetItemQueryIterator<Item>(
"SELECT * FROM c WHERE c.category = 'electronics'",
requestOptions: new QueryRequestOptions
{
PopulateIndexMetrics = true
}
);
var response = await query.ReadNextAsync();
Console.WriteLine($"RU Charge: {response.RequestCharge}");
Console.WriteLine($"Index Metrics: {response.IndexMetrics}");
Indexing Strategies¶
Index Types¶
Range Index (Default)¶
- Supports equality, range, ORDER BY
- All paths indexed by default
- Efficient for most queries
Spatial Index¶
{
"spatialIndexes": [
{
"path": "/location/*",
"types": ["Point", "Polygon"]
}
]
}
Query:
SELECT * FROM c
WHERE ST_DISTANCE(c.location, {
"type": "Point",
"coordinates": [-122.33, 47.61]
}) < 1000
Composite Index¶
{
"compositeIndexes": [
[
{"path": "/category", "order": "ascending"},
{"path": "/price", "order": "descending"},
{"path": "/rating", "order": "descending"}
]
]
}
Benefits: - Optimizes ORDER BY on multiple properties - Reduces RU cost significantly - Required for ORDER BY multiple properties
Indexing Policy¶
Default Policy¶
{
"indexingMode": "consistent",
"automatic": true,
"includedPaths": [
{"path": "/*"}
],
"excludedPaths": [
{"path": "/\"_etag\"/?"}
]
}
Custom Policy Example¶
{
"indexingMode": "consistent",
"automatic": true,
"includedPaths": [
{"path": "/category/?"},
{"path": "/price/?"},
{"path": "/tags/[]/?"}
],
"excludedPaths": [
{"path": "/*"},
{"path": "/\"_etag\"/?"}
],
"compositeIndexes": [
[
{"path": "/category", "order": "ascending"},
{"path": "/price", "order": "descending"}
]
],
"spatialIndexes": [
{"path": "/location/*", "types": ["Point"]}
]
}
Indexing Modes¶
Consistent (Default)¶
- Index updated synchronously
- Queries always consistent
- Write cost includes indexing
None¶
- No indexing
- Lowest write cost
- Only point reads possible
Indexing Best Practices¶
1. Exclude Unused Paths
// Reduce write RU cost
{
"excludedPaths": [
{"path": "/largeBlob/*"},
{"path": "/metadata/*"}
]
}
2. Include Only Queried Paths
// Selective inclusion
{
"includedPaths": [
{"path": "/id/?"},
{"path": "/name/?"},
{"path": "/category/?"}
],
"excludedPaths": [
{"path": "/*"}
]
}
3. Use Composite Indexes for ORDER BY Required for ORDER BY multiple properties.
4. Use Wildcards for Arrays
{"path": "/tags/[]/?"} // Index all array elements
5. Monitor Index Usage - Check query metrics - Use Index Metrics in response - Adjust policy based on patterns
Consistency Levels¶
Five Consistency Levels¶
1. Strong¶
Guarantees: - Linearizability - Reads always return latest write - Global strong consistency
Cost: - Highest read RU (2x) - Higher latency - Reduced availability during outages
Use Cases: - Financial transactions - Inventory management - Auction systems
2. Bounded Staleness¶
Guarantees: - Reads lag behind writes by: - K versions (e.g., 100,000 operations), OR - T time interval (e.g., 5 minutes) - Consistent prefix (reads in order)
Cost: - Higher read RU (2x) - Lower latency than Strong
Use Cases: - Score tracking - Status tracking - Workflows with acceptable lag
3. Session (Default)¶
Guarantees: - Read your own writes - Monotonic reads - Consistent within client session
Cost: - Balanced RU cost (1x) - Good performance
Use Cases: - Most applications (default choice) - Shopping carts - User profiles - Social media
4. Consistent Prefix¶
Guarantees: - Reads never see out-of-order writes - No gaps in write sequence - May see stale data
Cost: - Lower read RU (1x)
Use Cases: - Social media feeds - Status updates - Non-critical data
5. Eventual¶
Guarantees: - Lowest consistency - Reads may see any order - Eventually consistent
Cost: - Lowest read RU (0.5x) - Highest availability
Use Cases: - Product reviews - Comments - Non-critical aggregates
Setting Consistency¶
Account-Level Default¶
// Set via Portal or CLI
// Default for all operations
Request-Level Override¶
var response = await container.ReadItemAsync<Item>(
id: "item-123",
partitionKey: new PartitionKey("value"),
requestOptions: new ItemRequestOptions
{
ConsistencyLevel = ConsistencyLevel.Eventual
}
);
Note: Can only relax, not strengthen consistency
Consistency Decision Matrix¶
| Requirement | Recommended Level |
|---|---|
| Financial accuracy | Strong |
| Real-time inventory | Strong |
| User profile (own edits) | Session |
| Product catalog | Session |
| Leaderboard (acceptable lag) | Bounded Staleness |
| Social media feed | Consistent Prefix |
| Product reviews | Eventual |
Change Feed¶
Overview¶
Continuous log of all changes to container.
Characteristics: - Immutable, append-only - Ordered within partition - Persisted (retention: varies) - Available for all items and operations
Change Feed Modes¶
Latest Version Mode (Default)¶
- Returns final state of item
- Inserts and updates
- Deletes not included (use TTL or soft delete)
All Versions and Deletes Mode (Preview)¶
- Returns all intermediate states
- Includes deletes
- Full audit trail
Change Feed Processor¶
Basic Setup¶
Container monitoredContainer = client
.GetDatabase("sourceDb")
.GetContainer("sourceContainer");
Container leaseContainer = client
.GetDatabase("leaseDb")
.GetContainer("leases");
ChangeFeedProcessor processor = monitoredContainer
.GetChangeFeedProcessorBuilder<Item>("myProcessor", HandleChangesAsync)
.WithInstanceName("consumerInstance1")
.WithLeaseContainer(leaseContainer)
.Build();
await processor.StartAsync();
async Task HandleChangesAsync(
IReadOnlyCollection<Item> changes,
CancellationToken cancellationToken)
{
foreach (var item in changes)
{
// Process each change
Console.WriteLine($"Changed: {item.Id}");
}
}
Advanced Configuration¶
var processor = container
.GetChangeFeedProcessorBuilder<Item>("myProcessor", HandleChangesAsync)
.WithInstanceName("instance1")
.WithLeaseContainer(leaseContainer)
.WithStartTime(DateTime.UtcNow.AddHours(-1)) // Start from 1 hour ago
.WithPollInterval(TimeSpan.FromSeconds(5))
.WithMaxItems(100)
.WithLeaseConfiguration(
acquireInterval: TimeSpan.FromSeconds(13),
expirationInterval: TimeSpan.FromSeconds(60),
renewInterval: TimeSpan.FromSeconds(17))
.Build();
Change Feed Use Cases¶
1. Event-Driven Architecture¶
async Task HandleChangesAsync(IReadOnlyCollection<Order> changes)
{
foreach (var order in changes)
{
if (order.Status == "Placed")
{
await SendOrderConfirmationEmail(order);
await UpdateInventory(order);
await NotifyWarehouse(order);
}
}
}
2. Data Replication¶
async Task HandleChangesAsync(IReadOnlyCollection<Item> changes)
{
// Replicate to another container/database
foreach (var item in changes)
{
await targetContainer.UpsertItemAsync(item);
}
}
3. Materialized Views¶
async Task HandleChangesAsync(IReadOnlyCollection<Order> changes)
{
// Maintain aggregated view
foreach (var order in changes)
{
var summary = await GetCustomerSummary(order.CustomerId);
summary.TotalOrders++;
summary.TotalSpent += order.Amount;
await summaryContainer.UpsertItemAsync(summary);
}
}
4. Real-Time Analytics¶
async Task HandleChangesAsync(IReadOnlyCollection<Event> changes)
{
// Stream to analytics system
await analyticsClient.SendBatch(changes);
}
Change Feed vs Triggers¶
| Aspect | Change Feed | Triggers |
|---|---|---|
| Execution | External processor | Server-side |
| Scalability | Highly scalable | Limited |
| Reliability | Durable, resumable | Atomic with operation |
| Use Case | Async processing | Synchronous validation |
Monitoring and Diagnostics¶
Azure Monitor Integration¶
Metrics¶
Key Metrics: - Total Request Units - Total Requests - Storage Used - Availability - Normalized RU Consumption (per partition) - Physical Partition Throughput - Physical Partition Storage
Access: - Azure Portal β Metrics - Azure Monitor API - Application Insights
Diagnostic Logs¶
# Enable diagnostic settings
az monitor diagnostic-settings create \
--name CosmosDBDiagnostics \
--resource $resourceId \
--logs '[{"category":"DataPlaneRequests","enabled":true}]' \
--metrics '[{"category":"Requests","enabled":true}]' \
--workspace $workspaceId
Log Categories: - DataPlaneRequests - QueryRuntimeStatistics - PartitionKeyStatistics - ControlPlaneRequests - MongoRequests (if using MongoDB API)
Performance Monitoring¶
Request Diagnostics¶
var response = await container.ReadItemAsync<Item>(
id: "item-123",
partitionKey: new PartitionKey("value")
);
Console.WriteLine(response.Diagnostics);
// Includes: request timeline, regions contacted, retry info
Query Statistics¶
var queryable = container.GetItemLinqQueryable<Item>(
requestOptions: new QueryRequestOptions
{
PopulateIndexMetrics = true
}
);
var iterator = queryable.ToFeedIterator();
var response = await iterator.ReadNextAsync();
Console.WriteLine($"RU Charge: {response.RequestCharge}");
Console.WriteLine($"Index Metrics: {response.IndexMetrics}");
Hot Partition Detection¶
// Log Analytics query
AzureDiagnostics
| where ResourceProvider == "MICROSOFT.DOCUMENTDB"
| where Category == "PartitionKeyRUConsumption"
| summarize TotalRU = sum(todouble(requestCharge_s)) by partitionKey_s
| order by TotalRU desc
Alerts¶
// Example: Alert on high RU consumption
{
"alertName": "HighRUConsumption",
"metricName": "TotalRequestUnits",
"threshold": 1000000,
"timeAggregation": "Total",
"windowSize": "PT5M",
"severity": 2
}
Backup and Disaster Recovery¶
Backup Modes¶
Continuous Backup¶
Features: - Point-in-time restore (PITR) - Restore to any second in past 30 days - Self-service restore via Portal/CLI/PS - Accidental delete/update recovery
Limitations: - Additional cost (~⅕ of storage cost) - Not available for serverless - Longer restore times
Enable:
az cosmosdb create \
--name myaccount \
--resource-group myRG \
--backup-policy-type Continuous
Restore:
az cosmosdb sql database restore \
--account-name myaccount \
--resource-group myRG \
--name mydb \
--restore-timestamp "2024-01-15T10:00:00Z"
Periodic Backup (Default)¶
Features: - Automatic backups every 4 hours (default) - Retention: 8 hours (configurable to 720 hours/30 days) - Restore requires support ticket - Geo-redundant storage
Configuration:
az cosmosdb update \
--name myaccount \
--resource-group myRG \
--backup-interval 240 \
--backup-retention 48
Multi-Region Deployment¶
Write Regions¶
Single-Region Writes: - One write region - Multiple read regions - Automatic failover to read region
Multi-Region Writes: - Multiple write regions - Lower write latency globally - Conflict resolution policies
Enable Multi-Region:
await client.GetDatabase("mydb")
.SetThroughputAsync(
ThroughputProperties.CreateManualThroughput(10000),
new RequestOptions { EnableMultipleWriteLocations = true }
);
Conflict Resolution¶
Last Write Wins (Default):
{
"conflictResolutionPolicy": {
"mode": "LastWriterWins",
"conflictResolutionPath": "/_ts"
}
}
Custom (Stored Procedure):
{
"conflictResolutionPolicy": {
"mode": "Custom",
"conflictResolutionProcedure": "dbs/mydb/colls/mycoll/sprocs/resolver"
}
}
Manual:
{
"conflictResolutionPolicy": {
"mode": "Custom"
}
}
// Read from conflicts feed and resolve manually
Disaster Recovery¶
Automatic Failover¶
# Enable automatic failover
az cosmosdb update \
--name myaccount \
--resource-group myRG \
--enable-automatic-failover true
# Set failover priorities
az cosmosdb failover-priority-change \
--name myaccount \
--resource-group myRG \
--failover-policies "eastus=0 westus=1"
Manual Failover¶
az cosmosdb failover-priority-change \
--name myaccount \
--resource-group myRG \
--failover-policies "westus=0 eastus=1"
SLA and Availability¶
| Configuration | Availability SLA |
|---|---|
| Single region | 99.99% |
| Multiple read regions | 99.99% (read) |
| Single write region | 99.99% (write) |
| Multiple write regions | 99.999% (write) |
| Availability Zones | 99.995% (single region) |
Exam Tips¶
Key Concepts¶
Request Units: - 1 RU = 1 KB point read - Writes cost ~5-6 RUs per KB - Strong consistency costs 2x reads - Cross-partition queries cost more
Throughput: - Manual: Fixed RU/s - Autoscale: 0.1x to max - Serverless: Pay per RU - Database vs container level
Consistency: - Strong: Linearizable, highest cost - Session: Default, read-your-writes - Eventual: Lowest cost, eventual consistency
Indexing: - Default: All paths indexed - Composite: Required for multi-property ORDER BY - Exclude unused paths to reduce write cost
Change Feed: - Ordered within partition - Latest version mode (default) - Use for events, replication, views - Requires lease container
Backup: - Continuous: PITR, self-service restore - Periodic: Every 4 hours, support ticket - Multi-region for DR
Common Scenarios¶
High RU Cost: 1. Check query patterns (cross-partition?) 2. Review indexing policy (over-indexing?) 3. Optimize queries (SELECT *, ORDER BY?) 4. Consider caching
Slow Queries: 1. Use point reads when possible 2. Include partition key in WHERE 3. Add composite indexes 4. Avoid cross-partition queries
Hot Partition: 1. Review partition key choice 2. Use synthetic keys 3. Increase throughput 4. Monitor partition metrics
Multi-Region Setup: - Single write: Lower cost, automatic failover - Multi-write: Lower latency, conflict resolution - Session consistency: Most common - Automatic failover: Enable for HA
Decision Matrix¶
Throughput Model: - Predictable load β Manual provisioned - Variable load β Autoscale - Small/dev β Serverless
Consistency Level: - Financial/inventory β Strong - User profiles β Session (default) - Product reviews β Eventual
Backup Mode: - Accidental deletes concern β Continuous - Cost-sensitive β Periodic - Compliance requirements β Check retention
Quick Reference¶
RU Guidelines: - Point read: 1 RU/KB - Write: ~5 RU/KB - Query: 2.3+ RU minimum - Strong consistency: 2x reads
Throughput Scaling: - Manual: Scale up/down anytime - Autoscale: Instant scale up, gradual down - Per partition: Max 10K RU/s
Indexing Best Practices: - Exclude large, unused properties - Use composite for ORDER BY - Include only queried paths - Monitor with index metrics
Change Feed: - Lease container required - Horizontal scaling supported - At-least-once delivery - Ordered per partition
Study Focus¶
- Understand RU consumption patterns
- Master query optimization techniques
- Know indexing strategies and impact
- Understand consistency tradeoffs
- Learn change feed use cases
- Practice monitoring and diagnostics
- Know backup and DR options
Final Checklist¶
- RU consumption factors
- Throughput models (manual, autoscale, serverless)
- Query optimization techniques
- Indexing policy configuration
- Consistency level characteristics
- Change feed patterns
- Monitoring and diagnostics
- Backup modes and restore
- Multi-region configuration
- Performance troubleshooting