Configure throughput and consistency in C#
In this exercise, you apply the three levers from this module. Provision autoscale throughput for the Contoso workload’s bursty traffic. Set an account default consistency level, then relax an individual read below it when needed. Configure time to live so temporary records expire on their own, without a cleanup job. Use a disposable container for the container-wide expiration test to protect the shared product catalog. Along the way, you read the actual request-unit charge of an operation, which is the measurement that turns a throughput estimate into a real number.
This exercise takes approximately 30 minutes to complete.
Before you start
To complete this exercise, you need an Azure subscription with permission to create resources and assign roles.
If your lab environment isn’t set up yet, follow Set up your lab environment to install Visual Studio Code, Git, the Azure CLI, and PowerShell 7.
You also need the .NET 10 SDK or later installed.
Set up your Azure Cosmos DB resources
The core exercises reuse an account prepared with the core profile, not the two-item account from the first portal exercise. Before skipping setup, open Allfiles/Labs/Shared in PowerShell, sign in with az login, and set $resourceGroup, $location, and $accountName to your recorded values. Run ./verify.ps1 -ResourceGroup $resourceGroup -AccountName $accountName -LabProfile core and continue only when it succeeds. In Data Explorer, confirm 295 items in cosmicworks/product and 237 in cosmicworks/productMeta with SELECT VALUE COUNT(1) FROM c.
If you have no verified core account, follow the setup steps below. To add missing resources to an existing lab account, pass its explicit -AccountName to setup rather than using a different module’s name prefix. Reseeding restores canonical items but doesn’t remove extra items or reset container policies. Resolve mismatches before continuing; don’t reset a shared account automatically.
-
Start Visual Studio Code.
-
If you don’t have the lab code yet, clone the repository for DP-420: open the command palette with Ctrl+Shift+P, run Git: Clone, and enter the following URL. Choose a local folder when prompted. Otherwise, open the folder from your previous clone.
https://github.com/microsoftlearning/dp-420-cosmos-db-dev -
Once the repository is cloned, open that local folder in Visual Studio Code.
-
In the Explorer pane, browse to the Allfiles/Labs/Shared folder.
-
Open the context menu for the folder and select Open in Integrated Terminal. If the terminal isn’t PowerShell, select the dropdown beside the + in the terminal toolbar and choose PowerShell.
-
Sign in to the Azure CLI. A browser window opens so you can sign in to Azure.
az login -
Set variables for the resource group (If a resource group was provided by your lab environment, use that name) and region you want to use. Change either value if you prefer a different resource group name or region.
$resourceGroup = "ResourceGroup1" $location = "westus2" -
Run the setup script.
./setup.ps1 -ResourceGroup $resourceGroup -Location $location -NamePrefix dp420lab02 -LabProfile coreAzure Cosmos DB account names have to be globally unique, so the script builds one for you by adding six random characters to the prefix, giving a name like
dp420lab02a7f3k9. -
Wait for the script to finish. The whole script takes 5-10 minutes to run.
-
Record the Account name and Account endpoint values the script prints. You need the endpoint later in this exercise, and it looks like
https://<your-account-name>.documents.azure.com:443/.
The script creates the following resources:
| Resource | Configuration |
|---|---|
| Azure Cosmos DB account | API for NoSQL, with key-based authentication disabled |
cosmicworks database |
Holds the containers for the core lab profile |
product container |
Partitioned on /categoryId, autoscale up to 1,000 RU/s, loaded with 295 CosmicWorks products |
productMeta container |
Partitioned on /type, autoscale up to 1,000 RU/s, loaded with 237 metadata items |
leases container |
Partitioned on /id, manual throughput of 400 RU/s |
operations and bulkload containers |
Each partitioned on /categoryId, with its own autoscale maximum of 1,000 RU/s |
| Role assignment | Cosmos DB Built-in Data Contributor, granted to your signed-in identity |
Because key-based authentication is disabled, no key or connection string appears anywhere in this exercise. Every operation authenticates with the identity from your az login session, which is the recommended approach for new accounts.
📝 A new role assignment takes a few minutes to propagate. If a later step fails with a 403 error, wait a moment and try again.
Review the autoscale range
Autoscale scales between 10 percent of the maximum you set and that maximum. Confirm that range in the portal.
-
In a web browser, open the Azure portal, sign in, and go to your Azure Cosmos DB account.
-
In the resource menu, select Data Explorer.
-
In the Data Explorer pane, expand the
cosmicworksdatabase, select the product container, and then select Scale and Settings. -
Review the Scale section. The Autoscale option is selected, and the maximum throughput is 1,000 RU/s.
Note the description of the scaling range. The container scales between 100 RU/s and 1,000 RU/s based on real-time demand. Billing uses the highest throughput reached during each hour, with a minimum charge for 100 RU/s even when idle. With dynamic scaling, the bill combines the hourly peaks for each physical partition and region.
-
Change Maximum RU/s to
4000and select Save. Wait for the update to finish and confirm that the displayed range is 400 RU/s to 4,000 RU/s.The minimum rises to 400 RU/s. Raising the ceiling raises the floor, which is why a high autoscale maximum increases your baseline cost.
-
Set Maximum RU/s back to
1000and select Save. Wait for the update to finish and confirm that the displayed range returns to 100 RU/s to 1,000 RU/s. -
Open the context menu for product in the database tree and select New SQL Query. Confirm that the query tab belongs to product, then run the following query to confirm that the loaded count is 295:
SELECT COUNT(1) AS itemCount FROM c
Set the account default consistency level
The account default applies to every read unless a client or request relaxes it. Set it to strong so you can observe the cost difference when you relax it.
-
In the Settings menu, select Default consistency.
-
Select Strong, and then select Save.
-
Wait for the change to apply before continuing.
📝 Strong consistency requires a read from more than one replica to guarantee the latest write. That extra work shows up directly in the request charge you measure next.
Measure the request charge at each consistency level
Now connect from code and compare the request charge of the same read at strong and eventual consistency.
-
In a terminal, create a console project and add the required packages:
dotnet new console -o throughput-lab cd throughput-lab dotnet add package Microsoft.Azure.Cosmos dotnet add package Newtonsoft.Json dotnet add package Azure.Identity -
Open the folder in Visual Studio Code and replace the contents of Program.cs with the following code. Replace
<cosmos-endpoint>with the account endpoint the setup script printed:using Microsoft.Azure.Cosmos; using Azure.Identity; string endpoint = "<cosmos-endpoint>"; CosmosClient client = new(endpoint, new DefaultAzureCredential()); Container container = client.GetContainer("cosmicworks", "product"); string id = "0A7E57DA-C73F-467F-954F-17B7AFD6227E"; PartitionKey partitionKey = new("4F34E180-384D-42FC-AC10-FEC30227577F"); ItemResponse<dynamic> strongResponse = await container.ReadItemAsync<dynamic>(id, partitionKey); Console.WriteLine($"STRONG request charge:\t{strongResponse.RequestCharge:0.00} RUs");DefaultAzureCredentialpicks up the identity you signed in with through the Azure CLI, so no key appears in the code. TheRequestChargeproperty reports the exact RU cost of the read. -
Run the application:
dotnet run -
Review the output. The charge reflects strong consistency:
STRONG request charge: 2.00 RUs -
Now relax the consistency level for this single request. Add the following code to the end of Program.cs:
ItemRequestOptions options = new() { ConsistencyLevel = ConsistencyLevel.Eventual }; ItemResponse<dynamic> eventualResponse = await container.ReadItemAsync<dynamic>(id, partitionKey, requestOptions: options); Console.WriteLine($"EVENTUAL request charge:\t{eventualResponse.RequestCharge:0.00} RUs");Passing
ItemRequestOptionswith a weakerConsistencyLevelrelaxes this consistency one read below the account default. -
Run the application again:
dotnet run -
Compare the two charges:
STRONG request charge: 2.00 RUs EVENTUAL request charge: 1.00 RUsYour exact values might differ slightly, but the strong read costs about twice the eventual one. The eventual read is served from a single replica instead of requiring agreement across replicas. On a read-heavy workload running thousands of operations per second, relaxing the reads that tolerate staleness is a direct and substantial cost reduction.
-
The
ConsistencyLeveloverride works in one direction only: setting it above the account default isn’t supported. When using this override, set the account default to the strongest level required and relax the other reads instead. The separate previewReadConsistencyStrategyAPI discussed earlier has different behavior.
Restore the account default consistency
Strong consistency roughly doubles the cost of every read, so set the account back to Session.
-
Return to the Azure portal and open your Azure Cosmos DB account.
-
In the Settings menu, select Default consistency.
-
Select Session, and then select Save.
Session is the default level for a new account. Wait for the change to apply, then restart any application with an existing SDK client so it picks up the new default. Each new run of the console application or Python script creates a new client.
Configure time to live and observe expiry
Finally, configure automatic data retention on the container.
-
Return to the Azure portal and open the Data Explorer.
-
Expand the
cosmicworksdatabase, select theproductcontainer, and then select Scale and Settings. -
In the Settings tab, find Time to Live and select On (no default).
This setting corresponds to a
DefaultTimeToLivevalue of-1. It enables the TTL mechanism without expiring existing items, which lets individual items opt in to expiration. -
Select Save.
-
Select Items, and then select New Item. Add an item with a
ttlproperty set to 60 seconds, and then select Save:{ "id": "temp-record-001", "categoryId": "4F34E180-384D-42FC-AC10-FEC30227577F", "name": "Temporary record", "ttl": 60 } -
Select New SQL Query under the product container and run the following query:
SELECT * FROM c WHERE c.id = "temp-record-001"Initially, the query returns the item you just added.
-
Wait about 90 seconds, then run the query again.
The expired
temp-record-001item no longer appears in query results. Physical deletion runs in the background and might finish later. TheML Road Pedalitem remains, because it has nottlproperty and the container default is-1.The pattern for mixed retention in a single container is to enable Time to Live (TTL) on the container, then set
ttlonly on the items that should expire. -
In the product container’s Scale and Settings, change Time to Live back to Off and select Save. Wait for the update to finish.
⚠ Don’t set a positive default TTL on the shared product catalog. Expiration is measured from each item’s last modification, not from when TTL is enabled. Existing items older than the default TTL can expire immediately.
-
Run the temporary-item query again after TTL is Off. An expired item can reappear if background deletion is incomplete when you disable TTL. If
temp-record-001reappears, open Items, filter forWHERE c.id = "temp-record-001", and select that item. Verify its ID and categoryId match the test item, then select Delete and confirm. Run the query again and confirm that no item remains. Don’t delete catalog products. -
In Data Explorer, create a new container named ttl-demo in the existing cosmicworks database. Use
/categoryIdas the partition key and dedicated autoscale throughput with a maximum of 1,000 RU/s. If that name already exists, use a new, unused name for this test. -
Open the new container’s Scale and Settings. Set Time to Live to On, enter
60seconds, and select Save. -
In the new container, add this item without a
ttlproperty:{ "id": "temp-record-001", "categoryId": "4F34E180-384D-42FC-AC10-FEC30227577F", "name": "Temporary record" }In this disposable container, every item without its own
ttlexpires 60 seconds after its last modification. -
Open the context menu for the new container and select New SQL Query. Confirm that the query tab belongs to that container, then run
SELECT * FROM c WHERE c.id = "temp-record-001". Wait about 90 seconds, then run it again. The item no longer appears because it inherits the container’s default TTL. -
Delete only the disposable ttl-demo container, or the alternative name you used, to stop its throughput charges. Keep product and the other shared lab containers.
-
In product, confirm that Time to Live is Off, the temporary-item query returns no result, and
SELECT VALUE COUNT(1) FROM creturns 295 before reusing this catalog.
Clean up resources
When you finish the course, delete the resource group only if you created it and every resource in it can be removed. If your lab provided ResourceGroup1, skip this command and delete only the exercise resources you no longer need:
az group delete --name $resourceGroup --yes --no-wait
If your lab environment provided the resource group, delete only the Azure Cosmos DB account instead:
az cosmosdb delete --name <your-account-name> --resource-group $resourceGroup --yes
You configured all three cost and performance levers: autoscale throughput sized to bursty traffic, a consistency level relaxed per request where staleness is acceptable, and time to live for automatic retention. The request-charge comparison you measured shows how consistency affects read cost; workload measurements and the billing model guide throughput sizing.