↓ Skip to main content
  1. Certifications/
  2. DP-420 Study Hub/

Azure Cosmos DB SDK Patterns

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")
SettingWhat it doesExam cue
One CosmosClient per accountThe client is thread-safe and manages connections and address cachesHigh 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 failoverRead from the region closest to the app
ConnectionMode.DirectTCP straight to the replicas; the .NET v3 default and the best performanceLowest latency
ConnectionMode.GatewayEvery request goes over HTTPS through the gatewayReduce the number of network connections
AllowBulkExecution = trueGroups concurrent operations into fewer service callsLoad a large volume of items
MaxRetryAttemptsOnRateLimitedRequestsAutomatic retries on 429; 9 by defaultTune rate limit retries
MaxRetryWaitTimeOnRateLimitedRequestsCumulative retry wait; 30 seconds by defaultTune rate limit retries
EnableContentResponseOnWrite = false on ItemRequestOptionsThe service skips returning the item after a create or updateHeavy 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"])
ReadRequest charge
Point read of a 1 KB item1 RU
Point read of a 100 KB item10 RU
Query, for each physical partition it checksAbout 2.5 RU minimum, even when nothing matches
Query without the partition keyChecks every physical partition (a cross-partition query)
Any read under strong or bounded stalenessDouble 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 _etag on the item and ETag on the .NET response.
  • The .NET request option is IfMatchEtag on ItemRequestOptions. 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.NETPython opBehavior
AddPatchOperation.AddaddAdds the property, or replaces it if it exists. At an array index it inserts and shifts the rest; - appends
SetPatchOperation.SetsetLike Add, but at an existing array index it updates that element in place
ReplacePatchOperation.ReplacereplaceUpdate only; the path must already exist
RemovePatchOperation.RemoveremoveRemoves the property or array element; the path must exist
IncrementPatchOperation.IncrementincrAdds a positive or negative number; creates the field when it is missing
MovePatchOperation.MovemoveMoves 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 _rid stay read-only.
  • Conditional patch: a SQL-like FilterPredicate makes the whole patch apply only when the item matches.
  • Several items, one partition key: patch inside a transactional batch, with TransactionalBatchPatchItemRequestOptions for the filter.

Transactional batch versus bulk
#

Both send many operations at once. Only one of them is a transaction.

Transactional batchBulk execution
ScopeOne partition key in one containerAny partition keys
AtomicYes. All operations commit together, or none doEach operation succeeds or fails on its own
Limits100 operations, 2 MB payload, 5 seconds of executionThe SDK groups concurrent operations for you
HowCreateTransactionalBatch(partitionKey), then ExecuteAsync()AllowBulkExecution = true on the client, then Task.WhenAll
FailureThe failed operation returns its own status code; every other operation returns 424 Failed DependencyEach task reports its own result
Choose it forRelated writes that must commit together, such as an order and its line itemsLoading 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
#

CodeMeaningTypical cause
404Not FoundA point read for an id that is absent from that partition key
409ConflictA create with an id that already exists in the logical partition
412Precondition FailedThe ETag no longer matches, because another writer updated the item
424Failed DependencyAnother operation in the same transactional batch failed
429Too Many RequestsThroughput 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.

Related