Request Units and Query Cost Optimization
Understand what drives RU consumption in Cosmos DB, read costs from response headers, and apply five optimisation techniques to reduce query costs.
What are Request Units?
Think of RU/s as a currency for database operations. Every read, write, and query costs a certain number of βcoinsβ (RU). Cosmos DB gives you a budget (provisioned RU/s) per second. If you spend faster than your budget, requests get throttled (429 errors).
A simple point read of a 1KB document costs ~1 RU. Writing that same document costs ~5-10 RU. Complex queries can cost hundreds. Your job is to keep costs low.
Amaraβs cost problem
π‘ Amara at SensorFlow provisioned 100,000 RU/s for her sensor readings container. After a month, she notices some queries cost 500+ RU each β 500Γ more than a point read. With 500M daily events, inefficient queries are burning through her budget.
Factors that affect RU cost
| Factor | Impact | Example |
|---|---|---|
| Document size | Larger documents cost more to read/write | 1KB = ~1 RU read; 10KB = ~10 RU read |
| Property count | More indexed properties = higher write cost | 5 indexed props vs 20 indexed props |
| Consistency level | Strong/Bounded = 2Γ read cost | Session read = 1 RU; Strong read = 2 RU |
| Query complexity | Cross-partition, no index, aggregations cost more | Point read = 1 RU; cross-partition scan = 500+ RU |
| Index utilisation | Queries using indexes cost far less | Indexed filter = 3 RU; full scan = 300 RU |
| Response size | More results = more RU | 10 items = 5 RU; 1000 items = 50 RU |
Reading RU cost from response headers
Every Cosmos DB response includes the RU charge:
// Point read β check RU cost
ItemResponse<SensorReading> response = await container.ReadItemAsync<SensorReading>(
id: "reading-123",
partitionKey: new PartitionKey("device-A")
);
double ruCost = response.RequestCharge;
Console.WriteLine($"Point read cost: {ruCost} RU"); // ~1 RU for 1KB
// Query β check RU cost
QueryDefinition query = new QueryDefinition(
"SELECT * FROM c WHERE c.deviceId = @deviceId AND c.temperature > @threshold")
.WithParameter("@deviceId", "device-A")
.WithParameter("@threshold", 80);
FeedIterator<SensorReading> iterator = container.GetItemQueryIterator<SensorReading>(query);
double totalRU = 0;
while (iterator.HasMoreResults)
{
FeedResponse<SensorReading> page = await iterator.ReadNextAsync();
totalRU += page.RequestCharge;
Console.WriteLine($"Page RU: {page.RequestCharge}, Items: {page.Count}");
}
Console.WriteLine($"Total query cost: {totalRU} RU");
Exam tip: RequestCharge is per page
For queries that return multiple pages, RequestCharge is reported per page, not for the entire query. You must sum the charges across all pages to get the total cost. The exam tests this β a query returning 5 pages of results has a total cost thatβs the sum of all 5 page charges.
Five optimisation techniques
1. Use point reads when possible
// Point read: ~1 RU for 1KB (cheapest possible operation)
var response = await container.ReadItemAsync<SensorReading>(
id: "reading-123",
partitionKey: new PartitionKey("device-A")
);
// vs. Query for the same document: ~3 RU
// SELECT * FROM c WHERE c.id = 'reading-123' AND c.deviceId = 'device-A'
Rule: If you know the id and partition key, always use a point read instead of a query.
2. Scope queries to a single partition
-- Single-partition query (cheap): targets one partition
SELECT * FROM c WHERE c.deviceId = 'device-A' AND c.temperature > 80
-- Cross-partition query (expensive): fans out to ALL partitions
SELECT * FROM c WHERE c.temperature > 80
3. Project only needed fields
-- Returns entire document (all 20+ fields)
SELECT * FROM c WHERE c.deviceId = 'device-A'
-- Returns only needed fields (smaller response = fewer RU)
SELECT c.deviceId, c.temperature, c.timestamp FROM c WHERE c.deviceId = 'device-A'
4. Optimise indexing policy
Ensure your queryβs WHERE and ORDER BY fields are indexed (covered in Module 18).
5. Use pagination with MaxItemCount
QueryRequestOptions options = new QueryRequestOptions
{
MaxItemCount = 100 // limit items per page to control memory and RU per page
};
The capacity calculator
Microsoft provides an online Cosmos DB Capacity Calculator to estimate RU/s needs:
| Input | What to provide |
|---|---|
| Document size | Average size in KB |
| Reads/sec | Point reads + queries per second |
| Writes/sec | Creates + updates per second |
| Regions | Number of read/write regions |
| Indexing | Default or custom policy |
Exam tip: 429 response handling
When you exceed provisioned RU/s, Cosmos DB returns a 429 TooManyRequests response with a Retry-After header (in milliseconds). The official SDKs have built-in retry logic that automatically waits and retries.
Exam questions may ask: βWhat should you do when getting 429 errors?β β the answer depends on context:
- Transient: Let the SDK retry (built-in)
- Persistent: Increase provisioned RU/s or enable autoscale
- Query-specific: Optimise the query to reduce per-request RU cost
π¬ Video walkthrough
π¬ Video coming soon
Request Units and Query Cost β DP-420 Module 19
Request Units and Query Cost β DP-420 Module 19
~16 minFlashcards
Knowledge Check
Amara notices a query costing 500 RU per execution. The query is: SELECT * FROM c WHERE c.temperature > 80. What's the most likely cause of the high cost?
A developer reads a document using: SELECT * FROM c WHERE c.id = 'reading-123' AND c.deviceId = 'device-A'. The query costs 3.5 RU. How can they reduce the cost?
Amara's query returns results across 5 pages. Each page reports RequestCharge of 20 RU. What is the total RU cost?
Next up: Integrated Cache and Dedicated Gateway β how to add a server-side cache layer that reduces RU consumption for read-heavy workloads.