Verified against Microsoft Learn on September 29, 2026. Every method name, limit, and status code on this page comes from the SDK documentation below. Examples use the .NET SDK v3 and the Python azure-cosmos package.
.NET SDK best practices · .NET performance tips · Optimistic concurrency · Partial document update · Transactional batch · Bulk import · Python ContainerProxy
DP-420 asks you to read and complete SDK code, so this page puts each pattern in C# and Python side by side. Choose a language tab once and every example on the page follows it. The exam leans on C#, so learn the .NET names first.
The client: one per account, for the life of the app#
// Create once, register as a singleton, and reuse it for the life of the app.
CosmosClient client = new CosmosClient(
accountEndpoint,
new DefaultAzureCredential(),
new CosmosClientOptions
{
ApplicationPreferredRegions = new List<string> { Regions.EastUS2, Regions.WestUS2 },
ConnectionMode = ConnectionMode.Direct // the default in .NET v3
});
Container container = client.GetContainer("retail", "products");from azure.cosmos import CosmosClient
from azure.identity import DefaultAzureCredential
# Create once and reuse it for the life of the app.
client = CosmosClient(
url,
credential=DefaultAzureCredential(),
preferred_locations=["East US 2", "West US 2"],
)
container = client.get_database_client("retail").get_container_client("products")| Setting | What it does | Exam cue |
|---|---|---|
One CosmosClient per account | The client is thread-safe and manages connections and address caches | High connection counts or port exhaustion: confirm the client is a singleton |
ApplicationPreferredRegions or ApplicationRegion (Python: preferred_locations) | Sets the order of regions for reads and failover | Read from the region closest to the app |
ConnectionMode.Direct | TCP straight to the replicas; the .NET v3 default and the best performance | Lowest latency |
ConnectionMode.Gateway | Every request goes over HTTPS through the gateway | Reduce the number of network connections |
AllowBulkExecution = true | Groups concurrent operations into fewer service calls | Load a large volume of items |
MaxRetryAttemptsOnRateLimitedRequests | Automatic retries on 429; 9 by default | Tune rate limit retries |
MaxRetryWaitTimeOnRateLimitedRequests | Cumulative retry wait; 30 seconds by default | Tune rate limit retries |
EnableContentResponseOnWrite = false on ItemRequestOptions | The service skips returning the item after a create or update | Heavy create payloads where the app already holds the item |
Point reads versus queries#
When you know both the id and the partition key, a point read is the cheapest and fastest way to fetch an item.
// Point read: id + partition key. A 1 KB item costs 1 RU.
ItemResponse<Product> read = await container.ReadItemAsync<Product>(
id: "bike-042",
partitionKey: new PartitionKey("road-bikes"));
Console.WriteLine($"Point read: {read.RequestCharge} RU");
// Query: parameterized and scoped to one partition key.
QueryDefinition query = new QueryDefinition(
"SELECT * FROM products p WHERE p.category = @category AND p.price < @max")
.WithParameter("@category", "road-bikes")
.WithParameter("@max", 500);
using FeedIterator<Product> feed = container.GetItemQueryIterator<Product>(
query,
requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey("road-bikes") });
while (feed.HasMoreResults)
{
FeedResponse<Product> page = await feed.ReadNextAsync();
Console.WriteLine($"Page: {page.Count} items, {page.RequestCharge} RU");
}# Point read: id + partition key. A 1 KB item costs 1 RU.
item = container.read_item(item="bike-042", partition_key="road-bikes")
print(container.client_connection.last_response_headers["x-ms-request-charge"])
# Query: parameterized and scoped to one partition key.
results = container.query_items(
query="SELECT * FROM products p WHERE p.category = @category AND p.price < @max",
parameters=[
{"name": "@category", "value": "road-bikes"},
{"name": "@max", "value": 500},
],
partition_key="road-bikes",
)
for product in results:
print(product["name"])| Read | Request charge |
|---|---|
| Point read of a 1 KB item | 1 RU |
| Point read of a 100 KB item | 10 RU |
| Query, for each physical partition it checks | About 2.5 RU minimum, even when nothing matches |
| Query without the partition key | Checks every physical partition (a cross-partition query) |
| Any read under strong or bounded staleness | Double the charge |
Given an id and a partition key, choose ReadItemAsync over a query every time. The write side of the math, such as a replace costing twice an insert, is on the throughput sheet.
Optimistic concurrency with ETags#
Every item carries a system _etag that the server changes on every update. Send it back with a replace, and the write succeeds only if nobody else changed the item first. The SDK applies the check only when you set it explicitly in the request options.
ItemResponse<Product> read = await container.ReadItemAsync<Product>(
"bike-042", new PartitionKey("road-bikes"));
Product product = read.Resource;
product.price = 399.99m;
try
{
await container.ReplaceItemAsync(
product,
product.id,
new PartitionKey(product.category),
new ItemRequestOptions { IfMatchEtag = read.ETag });
}
catch (CosmosException ex) when (ex.StatusCode == HttpStatusCode.PreconditionFailed)
{
// 412: another writer changed the item. Read it again, reapply the change, retry.
}from azure.core import MatchConditions
from azure.cosmos import exceptions
item = container.read_item(item="bike-042", partition_key="road-bikes")
item["price"] = 399.99
try:
container.replace_item(
item=item["id"],
body=item,
etag=item["_etag"],
match_condition=MatchConditions.IfNotModified,
)
except exceptions.CosmosAccessConditionFailedError:
# 412: another writer changed the item. Read it again, reapply the change, retry.
pass- The property is
_etagon the item andETagon the .NET response. - The .NET request option is
IfMatchEtagonItemRequestOptions. A mismatch returns 412 Precondition Failed. - The recovery loop is always the same: read again, reapply, retry.
Partial document update (patch)#
Patch changes specific properties on the server, so the client skips the read and the full replace.
| Operation | .NET | Python op | Behavior |
|---|---|---|---|
| Add | PatchOperation.Add | add | Adds the property, or replaces it if it exists. At an array index it inserts and shifts the rest; - appends |
| Set | PatchOperation.Set | set | Like Add, but at an existing array index it updates that element in place |
| Replace | PatchOperation.Replace | replace | Update only; the path must already exist |
| Remove | PatchOperation.Remove | remove | Removes the property or array element; the path must exist |
| Increment | PatchOperation.Increment | incr | Adds a positive or negative number; creates the field when it is missing |
| Move | PatchOperation.Move | move | Moves the value at from to path; from must exist |
List<PatchOperation> operations = new()
{
PatchOperation.Set("/status", "shipped"),
PatchOperation.Increment("/revision", 1),
PatchOperation.Add("/tags/-", "priority"), // "-" appends to the array
PatchOperation.Remove("/draftNotes")
};
// Optional: apply the patch only when the item still matches this filter.
PatchItemRequestOptions options = new()
{
FilterPredicate = "FROM orders o WHERE o.status = 'packed'"
};
ItemResponse<Order> patched = await container.PatchItemAsync<Order>(
id: "order-1001",
partitionKey: new PartitionKey("customer-77"),
patchOperations: operations,
requestOptions: options);operations = [
{"op": "set", "path": "/status", "value": "shipped"},
{"op": "incr", "path": "/revision", "value": 1},
{"op": "add", "path": "/tags/-", "value": "priority"}, # "-" appends
{"op": "remove", "path": "/draftNotes"},
]
# Optional: apply the patch only when the item still matches this filter.
container.patch_item(
item="order-1001",
partition_key="customer-77",
patch_operations=operations,
filter_predicate="FROM orders o WHERE o.status = 'packed'",
)- Up to 10 operations in a single patch.
- System properties such as
_etag,_ts, and_ridstay read-only. - Conditional patch: a SQL-like
FilterPredicatemakes the whole patch apply only when the item matches. - Several items, one partition key: patch inside a transactional batch, with
TransactionalBatchPatchItemRequestOptionsfor the filter.
Transactional batch versus bulk#
Both send many operations at once. Only one of them is a transaction.
| Transactional batch | Bulk execution | |
|---|---|---|
| Scope | One partition key in one container | Any partition keys |
| Atomic | Yes. All operations commit together, or none do | Each operation succeeds or fails on its own |
| Limits | 100 operations, 2 MB payload, 5 seconds of execution | The SDK groups concurrent operations for you |
| How | CreateTransactionalBatch(partitionKey), then ExecuteAsync() | AllowBulkExecution = true on the client, then Task.WhenAll |
| Failure | The failed operation returns its own status code; every other operation returns 424 Failed Dependency | Each task reports its own result |
| Choose it for | Related writes that must commit together, such as an order and its line items | Loading or migrating a large volume of items |
PartitionKey customer = new PartitionKey("customer-77");
TransactionalBatch batch = container.CreateTransactionalBatch(customer)
.CreateItem(order)
.CreateItem(lineOne)
.PatchItem("customer-77-profile", new[] { PatchOperation.Increment("/orderCount", 1) });
using TransactionalBatchResponse response = await batch.ExecuteAsync();
if (response.IsSuccessStatusCode)
{
Order created = response.GetOperationResultAtIndex<Order>(0).Resource;
}
else
{
// Nothing was committed. The failing operation carries its own status code,
// such as 409 when a create meets an existing id; the rest report 424.
Console.WriteLine($"Batch rolled back: {response.StatusCode}");
}from azure.cosmos import exceptions
batch = [
("create", (order,)),
("create", (line_one,)),
("patch", ("customer-77-profile", [{"op": "incr", "path": "/orderCount", "value": 1}])),
]
try:
results = container.execute_item_batch(batch_operations=batch, partition_key="customer-77")
except exceptions.CosmosBatchOperationError as e:
# Nothing was committed. e.error_index points at the operation that failed.
failed = e.operation_responses[e.error_index]The .NET batch returns its result in the response, so check IsSuccessStatusCode. The Python batch raises CosmosBatchOperationError when an operation fails.
Bulk with the .NET SDK#
CosmosClient bulkClient = new CosmosClient(
accountEndpoint,
new DefaultAzureCredential(),
new CosmosClientOptions { AllowBulkExecution = true });
Container target = bulkClient.GetContainer("retail", "products");
List<Task> tasks = new(products.Count);
foreach (Product product in products)
{
tasks.Add(target.CreateItemAsync(product, new PartitionKey(product.category))
.ContinueWith(t =>
{
if (t.IsFaulted)
{
// Log this item and retry it; the other items are unaffected.
}
}));
}
await Task.WhenAll(tasks);AllowBulkExecution is a client setting, so it applies to every operation that client sends. The client groups the concurrent tasks into service calls and spreads them across partitions to make full use of the provisioned throughput.
Consistency and session tokens in code#
// Relax consistency for a single read. The classic override can only weaken the account default.
ItemRequestOptions relaxed = new() { ConsistencyLevel = ConsistencyLevel.Eventual };
// Read your own writes from another client: pass the session token along.
ItemResponse<Order> write = await container.CreateItemAsync(order, new PartitionKey(order.customerId));
string sessionToken = write.Headers.Session;
ItemResponse<Order> readBack = await container.ReadItemAsync<Order>(
order.id,
new PartitionKey(order.customerId),
new ItemRequestOptions { SessionToken = sessionToken });The SDK uses the most recent session token automatically within one client. Pass the token yourself only when the read comes from a different client instance, such as another web server behind a load balancer. The consistency sheet covers ReadConsistencyStrategy, the newer option that can also strengthen a read.
Status codes to recognize#
| Code | Meaning | Typical cause |
|---|---|---|
| 404 | Not Found | A point read for an id that is absent from that partition key |
| 409 | Conflict | A create with an id that already exists in the logical partition |
| 412 | Precondition Failed | The ETag no longer matches, because another writer updated the item |
| 424 | Failed Dependency | Another operation in the same transactional batch failed |
| 429 | Too Many Requests | Throughput exceeded. The SDK retries 9 times over up to 30 seconds, honoring the retry-after interval, then surfaces the 429 |
Practice the code
The official labs build most of these patterns step by step in both languages, and the free Azure Cosmos DB emulator lets you rerun them at no cost. Then test yourself in the exam simulator, where many items ask you to complete code like the samples on this page.

