Skip to content

Commit 00505de

Browse files
committed
Adding httpclient to best practices
1 parent b484d52 commit 00505de

File tree

1 file changed

+48
-3
lines changed

1 file changed

+48
-3
lines changed

articles/cosmos-db/nosql/best-practice-dotnet.md

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ author: StefArroyo
55
ms.service: cosmos-db
66
ms.subservice: nosql
77
ms.topic: how-to
8-
ms.date: 08/30/2022
8+
ms.date: 03/09/2023
99
ms.author: esarroyo
1010
ms.reviewer: mjbrown
1111
ms.custom: cosmos-db-video
@@ -29,12 +29,12 @@ Watch the video below to learn more about using the .NET SDK from an Azure Cosmo
2929
| <input type="checkbox"/> | Regions | Make sure to run your application in the same [Azure region](../distribute-data-globally.md) as your Azure Cosmos DB account, whenever possible to reduce latency. Enable 2-4 regions and replicate your accounts in multiple regions for [best availability](../distribute-data-globally.md). For production workloads, enable [automatic failover](../how-to-manage-database-account.md#configure-multiple-write-regions). In the absence of this configuration, the account will experience loss of write availability for all the duration of the write region outage, as manual failover won't succeed due to lack of region connectivity. To learn how to add multiple regions using the .NET SDK visit [here](tutorial-global-distribution.md) |
3030
| <input type="checkbox"/> | Availability and Failovers | Set the [ApplicationPreferredRegions](/dotnet/api/microsoft.azure.cosmos.cosmosclientoptions.applicationpreferredregions?view=azure-dotnet&preserve-view=true) or [ApplicationRegion](/dotnet/api/microsoft.azure.cosmos.cosmosclientoptions.applicationregion?view=azure-dotnet&preserve-view=true) in the v3 SDK, and the [PreferredLocations](/dotnet/api/microsoft.azure.documents.client.connectionpolicy.preferredlocations?view=azure-dotnet&preserve-view=true) in the v2 SDK using the [preferred regions list](./tutorial-global-distribution.md?tabs=dotnetv3%2capi-async#preferred-locations). During failovers, write operations are sent to the current write region and all reads are sent to the first region within your preferred regions list. For more information about regional failover mechanics, see the [availability troubleshooting guide](troubleshoot-sdk-availability.md). |
3131
| <input type="checkbox"/> | CPU | You may run into connectivity/availability issues due to lack of resources on your client machine. Monitor your CPU utilization on nodes running the Azure Cosmos DB client, and scale up/out if usage is high. |
32-
| <input type="checkbox"/> | Hosting | Use [Windows 64-bit host](performance-tips-query-sdk.md#use-local-query-plan-generation) processing for best performance, whenever possible. |
32+
| <input type="checkbox"/> | Hosting | Use [Windows 64-bit host](performance-tips-query-sdk.md#use-local-query-plan-generation) processing for best performance, whenever possible. For Direct mode latency-sensitive production workloads, we highly recommend using at least 4-cores and 8-GB memory VMs whenever possible.
3333
| <input type="checkbox"/> | Connectivity Modes | Use [Direct mode](sdk-connection-modes.md) for the best performance. For instructions on how to do this, see the [V3 SDK documentation](performance-tips-dotnet-sdk-v3.md#networking) or the [V2 SDK documentation](performance-tips.md#networking).|
3434
|<input type="checkbox"/> | Networking | If using a virtual machine to run your application, enable [Accelerated Networking](../../virtual-network/create-vm-accelerated-networking-powershell.md) on your VM to help with bottlenecks due to high traffic and reduce latency or CPU jitter. You might also want to consider using a higher end Virtual Machine where the max CPU usage is under 70%. |
3535
|<input type="checkbox"/> | Ephemeral Port Exhaustion | For sparse or sporadic connections, we set the [`IdleConnectionTimeout`](/dotnet/api/microsoft.azure.cosmos.cosmosclientoptions.idletcpconnectiontimeout?view=azure-dotnet&preserve-view=true) and [`PortReuseMode`](/dotnet/api/microsoft.azure.cosmos.cosmosclientoptions.portreusemode?view=azure-dotnet&preserve-view=true) to `PrivatePortPool`. The `IdleConnectionTimeout` property helps control the time after which unused connections are closed. This will reduce the number of unused connections. By default, idle connections are kept open indefinitely. The value set must be greater than or equal to 10 minutes. We recommended values between 20 minutes and 24 hours. The `PortReuseMode` property allows the SDK to use a small pool of ephemeral ports for various Azure Cosmos DB destination endpoints. |
3636
|<input type="checkbox"/> | Use Async/Await | Avoid blocking calls: `Task.Result`, `Task.Wait`, and `Task.GetAwaiter().GetResult()`. The entire call stack is asynchronous in order to benefit from [async/await](/dotnet/csharp/programming-guide/concepts/async/) patterns. Many synchronous blocking calls lead to [Thread Pool starvation](/archive/blogs/vancem/diagnosing-net-core-threadpool-starvation-with-perfview-why-my-service-is-not-saturating-all-cores-or-seems-to-stall) and degraded response times. |
37-
|<input type="checkbox"/> | End-to-End Timeouts | To get end-to-end timeouts, you'll need to use both `RequestTimeout` and `CancellationToken` parameters. For more details on timeouts with Azure Cosmos DB [visit](troubleshoot-dotnet-sdk-request-timeout.md) |
37+
|<input type="checkbox"/> | End-to-End Timeouts | To get end-to-end timeouts, you'll need to use both `RequestTimeout` and `CancellationToken` parameters. For more details on timeouts with Azure Cosmos DB [visit our timeout troubleshooting guide](troubleshoot-dotnet-sdk-request-timeout.md). |
3838
|<input type="checkbox"/> | Retry Logic | A transient error is an error that has an underlying cause that soon resolves itself. Applications that connect to your database should be built to expect these transient errors. To handle them, implement retry logic in your code instead of surfacing them to users as application errors. The SDK has built-in logic to handle these transient failures on retryable requests like read or query operations. For accounts configured with a single write region, the SDK won't retry on writes for transient failures as writes aren't idempotent. For accounts configured with multiple write regions, there are [some scenarios](troubleshoot-sdk-availability.md#transient-connectivity-issues-on-tcp-protocol) where the SDK will automatically retry writes on other regions. The SDK does allow users to configure retry logic for throttles. For details on which errors to retry on [visit](conceptual-resilient-sdk-applications.md#should-my-application-retry-on-errors) |
3939
|<input type="checkbox"/> | Caching database/collection names | Retrieve the names of your databases and containers from configuration or cache them on start. Calls like `ReadDatabaseAsync` or `ReadDocumentCollectionAsync` and `CreateDatabaseQuery` or `CreateDocumentCollectionQuery` will result in metadata calls to the service, which consume from the system-reserved RU limit. `CreateIfNotExist` should also only be used once for setting up the database. Overall, these operations should be performed infrequently. |
4040
|<input type="checkbox"/> | Bulk Support | In scenarios where you may not need to optimize for latency, we recommend enabling [Bulk support](https://devblogs.microsoft.com/cosmosdb/introducing-bulk-support-in-the-net-sdk/) for dumping large volumes of data. |
@@ -51,6 +51,51 @@ Watch the video below to learn more about using the .NET SDK from an Azure Cosmo
5151

5252
[!INCLUDE[cosmos-db-dotnet-sdk-diagnostics](../includes/dotnet-sdk-diagnostics.md)]
5353

54+
## Best practices for HTTP connections
55+
56+
The .NET SDK uses `HttpClient` to perform HTTP operations both data plane for Gateway mode and [metadata for Direct mode](sdk-connection-modes.md#direct-mode-connection-architecture). One of the [fundamentals of HttpClient](/dotnet/fundamentals/networking/http/httpclient-guidelines.md#dns-behavior) is to make sure the `HttpClient` can react to DNS changes on your account by **customizing the DNS cache timeout**. When the DNS cache is cleared, **connections are closed**. Our recommendation is that you customize this value according to your [connectivity mode](sdk-connection-modes.md) and workload to reduce potential impact of the DNS cache refresh to a minimum (a 5 minute value would be a good start for Direct mode clients).
57+
58+
You can inject your custom HttpClient through `CosmosClientOptions.HttpClientFactory`, for example:
59+
60+
```csharp
61+
// Use a Singleton instance of the SocketsHttpHandler, which you can share across any HttpClient in your application
62+
SocketsHttpHandler socketsHttpHandler = new SocketsHttpHandler();
63+
// Customize this value based on desired DNS refresh timer
64+
socketsHttpHandler.PooledConnectionLifetime = TimeSpan.FromMinutes(5);
65+
66+
CosmosClientOptions cosmosClientOptions = new CosmosClientOptions()
67+
{
68+
// Pass your customized SocketHttpHandler to be used by the CosmosClient
69+
// Make sure `disposeHandler` is `false`
70+
HttpClientFactory = () => new HttpClient(socketsHttpHandler, disposeHandler: false)
71+
};
72+
73+
// Use a Singleton instance of the CosmosClient
74+
return new CosmosClient("<connection-string>", cosmosClientOptions);
75+
```
76+
77+
If you use [.NET dependency injection](/dotnet/core/extensions/dependency-injection.md), you can simplify the Singleton process:
78+
79+
```csharp
80+
SocketsHttpHandler socketsHttpHandler = new SocketsHttpHandler();
81+
// Customize this value based on desired DNS refresh timer
82+
socketsHttpHandler.PooledConnectionLifetime = TimeSpan.FromMinutes(5);
83+
// Registering the Singleton SocketsHttpHandler lets you reuse it across any HttpClient in your application
84+
services.AddSingleton<SocketsHttpHandler>(socketsHttpHandler);
85+
86+
// Use a Singleton instance of the CosmosClient
87+
services.AddSingleton<CosmosClient>(serviceProvider =>
88+
{
89+
SocketsHttpHandler socketsHttpHandler = serviceProvider.GetRequiredService<SocketsHttpHandler>();
90+
CosmosClientOptions cosmosClientOptions = new CosmosClientOptions()
91+
{
92+
HttpClientFactory = () => new HttpClient(socketsHttpHandler, disposeHandler: false)
93+
};
94+
95+
return new CosmosClient("<connection-string>", cosmosClientOptions);
96+
});
97+
```
98+
5499
## Best practices when using Gateway mode
55100

56101
Increase `System.Net MaxConnections` per host when you use Gateway mode. Azure Cosmos DB requests are made over HTTPS/REST when you use Gateway mode. They're subject to the default connection limit per hostname or IP address. You might need to set `MaxConnections` to a higher value (from 100 through 1,000) so that the client library can use multiple simultaneous connections to Azure Cosmos DB. In .NET SDK 1.8.0 and later, the default value for `ServicePointManager.DefaultConnectionLimit` is 50. To change the value, you can set `Documents.Client.ConnectionPolicy.MaxConnectionLimit` to a higher value.

0 commit comments

Comments
 (0)