//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
namespace Microsoft.Azure.Cosmos
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.Common;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using Microsoft.Azure.Cosmos.FaultInjection;
using Microsoft.Azure.Cosmos.Fluent;
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Newtonsoft.Json;
///
/// Defines all the configurable options that the CosmosClient requires.
///
///
/// An example on how to configure the serialization option to ignore null values.
///
///
///
///
public class CosmosClientOptions
{
///
/// Default connection mode
///
private const ConnectionMode DefaultConnectionMode = ConnectionMode.Direct;
///
/// Default Protocol mode
///
private const Protocol DefaultProtocol = Protocol.Tcp;
private const string ConnectionStringAccountEndpoint = "AccountEndpoint";
private const string ConnectionStringAccountKey = "AccountKey";
private const string ConnectionStringDisableServerCertificateValidation = "DisableServerCertificateValidation";
private const ApiType DefaultApiType = ApiType.None;
///
/// Default request timeout
///
private int gatewayModeMaxConnectionLimit;
private CosmosSerializationOptions serializerOptions;
private CosmosSerializer serializerInternal;
private System.Text.Json.JsonSerializerOptions stjSerializerOptions;
private ConnectionMode connectionMode;
private Protocol connectionProtocol;
private TimeSpan? idleTcpConnectionTimeout;
private TimeSpan? openTcpConnectionTimeout;
private int? maxRequestsPerTcpConnection;
private int? maxTcpConnectionsPerEndpoint;
private PortReuseMode? portReuseMode;
private IWebProxy webProxy;
private Func httpClientFactory;
private string applicationName;
private IFaultInjector faultInjector;
private bool isCustomSerializerProvided;
///
/// Creates a new CosmosClientOptions
///
public CosmosClientOptions()
{
this.GatewayModeMaxConnectionLimit = ConnectionPolicy.Default.MaxConnectionLimit;
this.RequestTimeout = ConnectionPolicy.Default.RequestTimeout;
this.TokenCredentialBackgroundRefreshInterval = null;
this.ConnectionMode = CosmosClientOptions.DefaultConnectionMode;
this.ConnectionProtocol = CosmosClientOptions.DefaultProtocol;
this.ApiType = CosmosClientOptions.DefaultApiType;
this.CustomHandlers = new Collection();
this.CosmosClientTelemetryOptions = new CosmosClientTelemetryOptions();
this.SessionRetryOptions = new SessionRetryOptions();
}
///
/// Get or set user-agent suffix to include with every Azure Cosmos DB service interaction.
///
///
/// Setting this property after sending any request won't have any effect.
///
public string ApplicationName
{
get => this.applicationName;
set
{
try
{
HttpRequestMessage dummyMessage = new HttpRequestMessage();
dummyMessage.Headers.Add(HttpConstants.HttpHeaders.UserAgent, value);
}
catch (FormatException fme)
{
throw new ArgumentException($"Application name '{value}' is invalid.", fme);
}
this.applicationName = value;
}
}
///
/// Get or set session container for the client
///
internal ISessionContainer SessionContainer { get; set; }
///
/// hint which guide SDK-internal retry policies on how early to switch retries to a different region.
///
internal SessionRetryOptions SessionRetryOptions { get; private set; }
///
/// Gets or sets the location where the application is running. This will influence the SDK's choice for the Azure Cosmos DB service interaction.
///
///
///
/// During the CosmosClient initialization the account information, including the available regions, is obtained from the .
/// The CosmosClient will use the value of to populate the preferred list with the account available regions ordered by geographical proximity to the indicated region.
/// If the value of is not an available region in the account, the preferred list is still populated following the same mechanism but would not include the indicated region.
///
///
/// If during CosmosClient initialization, the is not reachable, the CosmosClient will attempt to recover and obtain the account information issuing requests to all ordered by proximity to the .
/// For more granular control over the selected regions or to define a list based on a custom criteria, use instead of .
///
///
/// See also Diagnose
/// and troubleshoot the availability of Cosmos SDKs for more details.
///
///
/// This configuration is an alternative to , either one can be set but not both.
///
///
///
/// If an account is configured with multiple regions including West US, East US, and West Europe, configuring a client like the below example would result in the CosmosClient generating a sorted preferred regions based on proximity to East US.
/// The CosmosClient will send requests to East US, if that region becomes unavailable, it will fallback to West US (second in proximity), and finally to West Europe if West US becomes unavailable.
///
///
///
///
///
/// High availability on regional outages
public string ApplicationRegion { get; set; }
///
/// Gets and sets the preferred regions for geo-replicated database accounts in the Azure Cosmos DB service.
///
///
///
/// During the CosmosClient initialization the account information, including the available regions, is obtained from the .
/// The CosmosClient will use the value of to populate the preferred list with the account available regions that intersect with its value.
/// If the value of contains regions that are not an available region in the account, the values will be ignored. If these invalid regions are added later to the account, the CosmosClient will use them if they are higher in the preference order.
///
///
/// If during CosmosClient initialization, the is not reachable, the CosmosClient will attempt to recover and obtain the account information issuing requests to the regions in in the order that they are listed.
///
///
/// See also Diagnose
/// and troubleshoot the availability of Cosmos SDKs for more details.
///
///
/// This configuration is an alternative to , either one can be set but not both.
///
///
///
///
/// (){ Regions.EastUS, Regions.WestUS }
/// };
///
/// CosmosClient client = new CosmosClient("endpoint", "key", clientOptions);
/// ]]>
///
///
/// High availability on regional outages
public IReadOnlyList ApplicationPreferredRegions { get; set; }
///
/// Gets and sets the custom endpoints to use for account initialization for geo-replicated database accounts in the Azure Cosmos DB service.
///
///
///
/// During the CosmosClient initialization the account information, including the available regions, is obtained from the .
/// Should the global endpoint become inaccessible, the CosmosClient will attempt to obtain the account information issuing requests to the custom endpoints provided in .
///
///
/// Nevertheless, this parameter remains optional and is recommended for implementation when a customer has configured an endpoint with a custom DNS hostname
/// (instead of accountname-region.documents.azure.com) etc. for their Cosmos DB account.
///
///
/// See also Diagnose
/// and troubleshoot the availability of Cosmos SDKs for more details.
///
///
///
///
/// ()
/// {
/// new Uri("custom.p-1.documents.azure.com"),
/// new Uri("custom.p-2.documents.azure.com")
/// }
/// };
///
/// CosmosClient client = new CosmosClient("endpoint", "key", clientOptions);
/// ]]>
///
///
/// High availability on regional outages
public IEnumerable AccountInitializationCustomEndpoints { get; set; }
///
/// Get or set the maximum number of concurrent connections allowed for the target
/// service endpoint in the Azure Cosmos DB service.
///
///
/// This setting is only applicable in Gateway mode.
/// The SDK sets the following on the underlying SocketsHttpHandler:
///
/// - EnableMultipleHttp2Connections = true — allows additional HTTP/2 TCP connections
/// to be opened when the maximum concurrent streams limit on an existing connection is reached.
/// - KeepAlivePingDelay = 1 second — sends HTTP/2 PING frames after 1 second
/// of inactivity to detect broken connections in the pool.
/// - KeepAlivePingTimeout = 2 seconds — marks a connection as dead if no PONG
/// response is received within 2 seconds.
/// - KeepAlivePingPolicy = Always — sends pings even for idle connections, which
/// is critical for detecting broken connections that remain in the pool.
///
/// This property controls the upper bound on the total number of connections per server endpoint.
/// When using a custom , configure these properties
/// directly on your SocketsHttpHandler for equivalent behavior.
///
///
/// Using the SDK-managed handler with a custom connection limit:
///
///
///
/// When providing a custom HttpClientFactory, set the properties on SocketsHttpHandler directly:
///
/// new HttpClient(handler, disposeHandler: false)
/// };
/// ]]>
///
///
/// Default value is 50.
///
public int GatewayModeMaxConnectionLimit
{
get => this.gatewayModeMaxConnectionLimit;
set
{
if (value <= 0)
{
throw new ArgumentOutOfRangeException(nameof(value));
}
if (this.HttpClientFactory != null && value != ConnectionPolicy.Default.MaxConnectionLimit)
{
throw new ArgumentException($"{nameof(this.httpClientFactory)} can not be set along with {nameof(this.GatewayModeMaxConnectionLimit)}. This must be set on the HttpClientHandler.MaxConnectionsPerServer property.");
}
this.gatewayModeMaxConnectionLimit = value;
}
}
///
/// Gets the request timeout in seconds when connecting to the Azure Cosmos DB service.
/// The number specifies the time to wait for response to come back from network peer.
///
/// Default value is 6 seconds.
///
public TimeSpan RequestTimeout { get; set; }
///
/// Gets or sets the request timeout for inference service operations (e.g., semantic reranking).
/// The number specifies the time to wait for a response from the inference service before the request is cancelled.
/// This is a single-attempt timeout with no retries.
///
/// Default value is 5 seconds.
///
/// This timeout is specific to inference service operations and is separate from the standard .
/// If the request does not complete within the specified duration, a with status 408 (Request Timeout) is thrown.
/// No retries are attempted on timeout.
///
#if PREVIEW
public
#else
internal
#endif
TimeSpan InferenceRequestTimeout { get; set; } = InferenceService.DefaultInferenceRequestTimeout;
///
/// The SDK does a background refresh based on the time interval set to refresh the token credentials.
/// This avoids latency issues because the old token is used until the new token is retrieved.
///
///
/// The recommended minimum value is 5 minutes. The default value is 50% of the token expire time.
///
public TimeSpan? TokenCredentialBackgroundRefreshInterval { get; set; }
///
/// Gets the handlers run before the process
///
///
[JsonConverter(typeof(ClientOptionJsonConverter))]
public Collection CustomHandlers { get; }
///
/// Get or set the connection mode used by the client when connecting to the Azure Cosmos DB service.
///
///
/// Default value is
///
///
/// For more information, see Connection policy: Use direct connection mode.
///
///
///
public ConnectionMode ConnectionMode
{
get => this.connectionMode;
set
{
if (value == ConnectionMode.Gateway)
{
this.ConnectionProtocol = Protocol.Https;
}
else if (value == ConnectionMode.Direct)
{
this.connectionProtocol = Protocol.Tcp;
}
this.ValidateDirectTCPSettings();
this.connectionMode = value;
}
}
///
/// This can be used to weaken the database account consistency level for read operations.
/// If this is not set the database account consistency level will be used for all requests.
///
public ConsistencyLevel? ConsistencyLevel { get; set; }
///
/// Gets or sets the to be used for read operations.
///
///
/// When set, this takes precedence over for read and query operations.
/// If also set at request level (e.g., in ),
/// the request-level value is used.
///
/// is only valid for accounts configured with Strong consistency.
///
///
#if PREVIEW
public
#else
internal
#endif
ReadConsistencyStrategy? ReadConsistencyStrategy { get; set; }
///
/// Gets or sets the client-wide default used to generate
/// query-time vector embeddings for hybrid and vector-search queries.
///
[JsonIgnore]
#if PREVIEW
public
#else
internal
#endif
ICosmosEmbeddingGenerator EmbeddingGenerator { get; set; }
///
/// Sets the priority level for requests created using cosmos client.
///
///
/// If priority level is also set at request level in , that priority is used.
/// If is set to true in CosmosClientOptions, priority level set on the CosmosClient is used.
///
///
public PriorityLevel? PriorityLevel { get; set; }
///
/// Gets or sets the maximum number of retries in the case where the request fails
/// because the Azure Cosmos DB service has applied rate limiting on the client.
///
///
/// The default value is 9. This means in the case where the request is rate limited,
/// the same request will be issued for a maximum of 10 times to the server before
/// an error is returned to the application.
///
/// If the value of this property is set to 0, there will be no automatic retry on rate
/// limiting requests from the client and the exception needs to be handled at the
/// application level.
///
///
///
/// When a client is sending requests faster than the allowed rate,
/// the service will return HttpStatusCode 429 (Too Many Requests) to rate limit the client. The current
/// implementation in the SDK will then wait for the amount of time the service tells it to wait and
/// retry after the time has elapsed.
///
///
/// For more information, see Handle rate limiting/request rate too large.
///
///
///
public int? MaxRetryAttemptsOnRateLimitedRequests { get; set; }
///
/// Gets or sets the maximum retry time in seconds for the Azure Cosmos DB service.
///
///
/// The default value is 30 seconds.
///
///
///
/// The minimum interval is seconds. Any interval that is smaller will be ignored.
///
///
/// When a request fails due to a rate limiting error, the service sends back a response that
/// contains a value indicating the client should not retry before the time period has
/// elapsed.
///
/// This property allows the application to set a maximum wait time for all retry attempts.
/// If the cumulative wait time exceeds the this value, the client will stop retrying and return the error to the application.
///
///
/// For more information, see Handle rate limiting/request rate too large.
///
///
///
public TimeSpan? MaxRetryWaitTimeOnRateLimitedRequests { get; set; }
///
/// Gets or sets the boolean to only return the headers and status code in
/// the Cosmos DB response for write item operation like Create, Upsert, Patch and Replace.
/// Setting the option to false will cause the response to have a null resource. This reduces networking and CPU load by not sending
/// the resource back over the network and serializing it on the client.
///
///
/// This is optimal for workloads where the returned resource is not used.
/// This option can be overriden by similar property in ItemRequestOptions and TransactionalBatchItemRequestOptions
///
///
///
///
public bool? EnableContentResponseOnWrite { get; set; }
///
/// Sets the for the System.Text.Json serializer.
/// Note that if this option is provided, then the SDK will use the System.Text.Json as the default serializer and set
/// the serializer options as the constructor args.
///
///
/// An example on how to configure the System.Text.Json serializer options to ignore null values
///
///
///
///
[JsonConverter(typeof(ClientOptionJsonConverter))]
public System.Text.Json.JsonSerializerOptions UseSystemTextJsonSerializerWithOptions
{
get => this.stjSerializerOptions;
set
{
if (this.Serializer != null || this.SerializerOptions != null)
{
throw new ArgumentException(
$"{nameof(this.UseSystemTextJsonSerializerWithOptions)} is not compatible with {nameof(this.Serializer)} or {nameof(this.SerializerOptions)}. Only one can be set. ");
}
this.stjSerializerOptions = value;
this.serializerInternal = new CosmosSystemTextJsonSerializer(
this.stjSerializerOptions);
}
}
///
/// Gets or sets the advanced replica selection flag. The advanced replica selection logic keeps track of the replica connection
/// status, and based on status, it prioritizes the replicas which show healthy stable connections, so that the requests can be sent
/// confidently to the particular replica. This helps the cosmos client to become more resilient and effective to any connectivity issues.
/// The default value for this parameter is 'false'.
///
///
/// This is optimal for latency-sensitive workloads. Does not apply if is used.
///
internal bool? EnableAdvancedReplicaSelectionForTcp { get; set; }
///
/// Gets or sets stack trace optimization to reduce stack trace proliferation in high-concurrency scenarios where exceptions are frequently thrown.
/// When enabled, critical SDK components optimize exception handling to minimize performance overhead.
/// The default value is 'true'.
///
internal bool EnableAsyncCacheExceptionNoSharing { get; set; } = true;
///
/// Gets or sets the boolean flag to skip converting a text stream to binary and vice versa. When enabled, the request and response stream
/// would not be converted to the desired target serialization type and will act just like a pass through. This client option will
/// remain internal only since the consumer of this flag will be the internal components of the cosmos db ecosystem.
/// The default value for this parameter is 'false'.
///
internal bool EnableStreamPassThrough { get; set; } = false;
///
/// Gets or sets a value indicating whether to use length-aware range comparators for EPK range comparisons.
/// Length-aware range comparators were introduced in Range class to handle EPK range comparisons correctly
/// in the case of a container's physical partition set consisting of fully and partially specified EPK values.
/// By default, length-aware range comparator is enabled. Refer to Range.cs in Msdata project for more details.
/// Range.LengthAwareMinComparer/LengthAwareMaxComparer.
/// Setting the value to false will disable length-aware range comparator and switch to using the regular
/// Range.MinComparer/MaxComparer.
///
///
/// The default value is true.
///
internal bool UseLengthAwareRangeComparer { get; set; } =
#if !INTERNAL
true;
#else
false;
#endif
///
/// (Direct/TCP) Controls the amount of idle time after which unused connections are closed.
///
///
/// By default, idle connections are kept open indefinitely. Value must be greater than or equal to 10 minutes. Recommended values are between 20 minutes and 24 hours.
///
///
/// Mainly useful for sparse infrequent access to a large database account.
///
public TimeSpan? IdleTcpConnectionTimeout
{
get => this.idleTcpConnectionTimeout;
set
{
this.idleTcpConnectionTimeout = value;
this.ValidateDirectTCPSettings();
}
}
///
/// (Direct/TCP) Controls the amount of time allowed for trying to establish a connection.
///
///
/// The default timeout is 5 seconds. For latency sensitive applications that prefer to retry faster, a recommended value of 1 second can be used.
///
///
/// When the time elapses, the attempt is cancelled and an error is returned. Longer timeouts will delay retries and failures.
///
public TimeSpan? OpenTcpConnectionTimeout
{
get => this.openTcpConnectionTimeout;
set
{
this.openTcpConnectionTimeout = value;
this.ValidateDirectTCPSettings();
}
}
///
/// (Direct/TCP) Controls the number of requests allowed simultaneously over a single TCP connection. When more requests are in flight simultaneously, the direct/TCP client will open additional connections.
///
///
/// The default settings allow 30 simultaneous requests per connection.
/// Do not set this value lower than 4 requests per connection or higher than 50-100 requests per connection.
/// The former can lead to a large number of connections to be created.
/// The latter can lead to head of line blocking, high latency and timeouts.
///
///
/// Applications with a very high degree of parallelism per connection, with large requests or responses, or with very tight latency requirements might get better performance with 8-16 requests per connection.
///
public int? MaxRequestsPerTcpConnection
{
get => this.maxRequestsPerTcpConnection;
set
{
this.maxRequestsPerTcpConnection = value;
this.ValidateDirectTCPSettings();
}
}
///
/// (Direct/TCP) Controls the maximum number of TCP connections that may be opened to each Cosmos DB back-end.
/// Together with MaxRequestsPerTcpConnection, this setting limits the number of requests that are simultaneously sent to a single Cosmos DB back-end(MaxRequestsPerTcpConnection x MaxTcpConnectionPerEndpoint).
///
///
/// The default value is 65,535. Any positive value is accepted, allowing applications to constrain the connection pool size when needed; values of 16 or greater are recommended.
///
public int? MaxTcpConnectionsPerEndpoint
{
get => this.maxTcpConnectionsPerEndpoint;
set
{
this.maxTcpConnectionsPerEndpoint = value;
this.ValidateDirectTCPSettings();
}
}
///
/// (Direct/TCP) Controls the client port reuse policy used by the transport stack.
///
///
/// The default value is PortReuseMode.ReuseUnicastPort.
///
///
/// ReuseUnicastPort and PrivatePortPool are not mutually exclusive.
/// When PrivatePortPool is enabled, the client first tries to reuse a port it already has.
/// It falls back to allocating a new port if the initial attempts failed. If this fails, too, the client then falls back to ReuseUnicastPort.
///
public PortReuseMode? PortReuseMode
{
get => this.portReuseMode;
set
{
this.portReuseMode = value;
this.ValidateDirectTCPSettings();
}
}
///
/// (Gateway/Https) Get or set the proxy information used for web requests.
///
[JsonIgnore]
public IWebProxy WebProxy
{
get => this.webProxy;
set
{
if (value != null && this.HttpClientFactory != null)
{
throw new ArgumentException($"{nameof(this.WebProxy)} cannot be set along {nameof(this.HttpClientFactory)}");
}
this.webProxy = value;
}
}
///
/// Get to set optional serializer options.
///
///
/// An example on how to configure the serialization option to ignore null values
///
///
///
///
public CosmosSerializationOptions SerializerOptions
{
get => this.serializerOptions;
set
{
if (this.Serializer != null || this.UseSystemTextJsonSerializerWithOptions != null)
{
throw new ArgumentException(
$"{nameof(this.SerializerOptions)} is not compatible with {nameof(this.Serializer)} or {nameof(this.UseSystemTextJsonSerializerWithOptions)}. Only one can be set. ");
}
this.serializerOptions = value;
}
}
///
/// Get to set an optional JSON serializer. The client will use it to serialize or de-serialize user's cosmos request/responses.
/// SDK owned types such as DatabaseProperties and ContainerProperties will always use the SDK default serializer.
///
///
/// An example on how to set a custom serializer. For basic serializer options look at CosmosSerializationOptions
///
///
///
///
[JsonConverter(typeof(ClientOptionJsonConverter))]
public CosmosSerializer Serializer
{
get => this.serializerInternal;
set
{
if (this.SerializerOptions != null || this.UseSystemTextJsonSerializerWithOptions != null)
{
throw new ArgumentException(
$"{nameof(this.Serializer)} is not compatible with {nameof(this.SerializerOptions)} or {nameof(this.UseSystemTextJsonSerializerWithOptions)}. Only one can be set. ");
}
this.isCustomSerializerProvided = true;
this.serializerInternal = value;
}
}
///
/// Limits the operations to the provided endpoint on the CosmosClient.
///
///
/// Default value is false.
///
///
/// When the value of this property is false, the SDK will automatically discover write and read regions, and use them when the configured application region is not available.
/// When set to true, availability is limited to the endpoint specified on the CosmosClient constructor.
/// Defining the or is not allowed when setting the value to true.
///
/// High availability
public bool LimitToEndpoint { get; set; } = false;
///
/// Allows optimistic batching of requests to service. Setting this option might impact the latency of the operations. Hence this option is recommended for non-latency sensitive scenarios only.
///
/// The use of Resource Tokens scoped to a Partition Key as an authentication mechanism when Bulk is enabled is not recommended as it reduces the potential throughput benefit
///
///
public bool AllowBulkExecution { get; set; }
///
/// Gets or sets the flag to enable address cache refresh on TCP connection reset notification.
///
///
/// Does not apply if is used.
///
///
/// The default value is true
///
public bool EnableTcpConnectionEndpointRediscovery { get; set; } = true;
///
/// Gets or sets a delegate to use to obtain an HttpClient instance to be used for HTTPS communication.
///
///
///
/// HTTPS communication is used when is set to for all operations and when is (default) for metadata operations.
///
///
/// Useful in scenarios where the application is using a pool of HttpClient instances to be shared, like ASP.NET Core applications with IHttpClientFactory or Blazor WebAssembly applications.
///
///
/// For .NET core applications the default GatewayConnectionLimit will be ignored. It must be set on the HttpClientHandler.MaxConnectionsPerServer to limit the number of connections
///
///
[JsonIgnore]
public Func HttpClientFactory
{
get => this.httpClientFactory;
set
{
if (value != null && this.WebProxy != null)
{
throw new ArgumentException($"{nameof(this.HttpClientFactory)} cannot be set along {nameof(this.WebProxy)}");
}
if (this.GatewayModeMaxConnectionLimit != ConnectionPolicy.Default.MaxConnectionLimit)
{
throw new ArgumentException($"{nameof(this.httpClientFactory)} can not be set along with {nameof(this.GatewayModeMaxConnectionLimit)}. This must be set on the HttpClientHandler.MaxConnectionsPerServer property.");
}
this.httpClientFactory = value;
}
}
///
/// Availability Strategy to be used for periods of high latency
///
/// ///
/// An example on how to set an availability strategy custom serializer.
///
/// { "East US", "Central US", "West US" } )
/// .WithAvailabilityStrategy(
/// AvailabilityStrategy.CrossRegionHedgingStrategy(
/// threshold: TimeSpan.FromMilliseconds(500),
/// thresholdStep: TimeSpan.FromMilliseconds(100)
/// ))
/// .Build();
/// ]]>
///
///
///
/// The availability strategy in the example is a Cross Region Hedging Strategy.
/// These strategies take two values, a threshold and a threshold step.When a request that is sent
/// out takes longer than the threshold time, the SDK will hedge to the second region in the application preferred regions list.
/// If a response from either the primary request or the first hedged request is not received
/// after the threshold step time, the SDK will hedge to the third region and so on.
///
public AvailabilityStrategy AvailabilityStrategy { get; set; }
///
/// Provides SessionTokenMismatchRetryPolicy optimization through customer supplied region switch hints,
/// which guide SDK-internal retry policies on how early to fallback to the next applicable region.
/// With a single-write-region account the next applicable region is the write-region, with a
/// multi-write-region account the next applicable region is the next region in the order of effective
/// preferred regions (same order also used for read/query operations).
///
#if PREVIEW
public
#else
internal
#endif
bool EnableRemoteRegionPreferredForSessionRetry
{
get => this.SessionRetryOptions.RemoteRegionPreferred;
set => this.SessionRetryOptions.RemoteRegionPreferred = value;
}
///
/// Enable partition level circuit breaker (aka PPCB). For compute gateway use case, by default per partition automatic failover will be disabled, so does the PPCB.
/// If compute gateway chooses to enable PPAF, then the .NET SDK will enable PPCB by default, which will improve the read availability and latency. This would mean
/// when PPAF is enabled, the SDK will automatically enable PPCB as well.
///
internal bool EnablePartitionLevelCircuitBreaker { get; set; } = ConfigurationManager.IsPartitionLevelCircuitBreakerEnabled(defaultValue: false);
///
/// Flag from gateway to disable partition level failover. Normally, the SDK will enable partition level failover based on the account settings.
/// This flag will be used internally by the compute gateway as by default it will disable partition level failover.
///
/// The default value for this parameter is 'false'.
///
internal bool DisablePartitionLevelFailover { get; set; } = false;
///
/// Quorum Read allowed with eventual consistency account or consistent prefix account.
///
internal bool EnableUpgradeConsistencyToLocalQuorum { get; set; } = false;
///
/// Gets or sets the connection protocol when connecting to the Azure Cosmos service.
///
///
/// Default value is .
///
///
/// This setting is not used when is set to .
/// Gateway mode only supports HTTPS.
/// For more information, see Connection policy: Use the HTTPS protocol.
///
internal Protocol ConnectionProtocol
{
get => this.connectionProtocol;
set
{
this.ValidateDirectTCPSettings();
this.connectionProtocol = value;
}
}
///
/// The event handler to be invoked before the request is sent.
///
internal EventHandler SendingRequestEventArgs { get; set; }
///
/// (Optional) transport interceptor factory
///
internal Func TransportClientHandlerFactory { get; set; }
///
/// A callback delegate to do custom certificate validation for both HTTP and TCP.
///
///
///
/// Emulator: To ignore SSL Certificate please suffix connectionstring with "DisableServerCertificateValidation=True;".
/// When CosmosClientOptions.HttpClientFactory is used, SSL certificate needs to be handled appropriately.
/// NOTE: DO NOT use the `DisableServerCertificateValidation` flag in production (only for emulator)
///
///
public Func ServerCertificateCustomValidationCallback { get; set; }
///
/// Real call back that will be hooked down-stream to the transport clients (both http and tcp).
/// NOTE: All down stream real-usage should come through this API only and not through the public API.
///
/// Test hook DisableServerCertificateValidationInvocationCallback
/// - When configured will invoke it when ever custom validation is done
///
internal Func GetServerCertificateCustomValidationCallback()
{
if (this.DisableServerCertificateValidation)
{
if (this.DisableServerCertificateValidationInvocationCallback == null)
{
return this.ServerCertificateCustomValidationCallback ?? ((_, _, _) => true);
}
else
{
return (X509Certificate2 cert, X509Chain chain, SslPolicyErrors policyErrors) =>
{
bool bValidationResult = true;
if (this.ServerCertificateCustomValidationCallback != null)
{
bValidationResult = this.ServerCertificateCustomValidationCallback(cert, chain, policyErrors);
}
this.DisableServerCertificateValidationInvocationCallback?.Invoke();
return bValidationResult;
};
}
}
return this.ServerCertificateCustomValidationCallback;
}
internal Action DisableServerCertificateValidationInvocationCallback { get; set; }
///
/// API type for the account
///
internal ApiType ApiType { get; set; }
///
/// Optional store client factory instance to use for all transport requests.
///
internal IStoreClientFactory StoreClientFactory { get; set; }
///
/// Gets or sets the initial delay retry time in milliseconds for the Azure Cosmos DB service
/// for requests that hit RetryWithExceptions. This covers errors that occur due to concurrency errors in the store.
///
///
/// The default value is 1 second. For an example on how to set this value, please refer to .
///
///
///
/// When a request fails due to a RetryWith error, the client delays and retries the request. This configures the client
/// to delay the time specified before retrying the request.
///
///
internal int? InitialRetryForRetryWithMilliseconds { get; set; }
///
/// Gets or sets the maximum delay retry time in milliseconds for the Azure Cosmos DB service
/// for requests that hit RetryWithExceptions. This covers errors that occur due to concurrency errors in the store.
///
///
/// The default value is 30 seconds. For an example on how to set this value, please refer to .
///
///
///
/// When a request fails due to a RetryWith error, the client delays and retries the request. This configures the maximum time
/// the client should delay before failing the request.
///
///
internal int? MaximumRetryForRetryWithMilliseconds { get; set; }
///
/// Gets or sets the interval to salt retry with value. This will spread the retry values from 1..n from the exponential back-off
/// subscribed.
///
///
/// The default value is to not salt.
///
///
///
/// When a request fails due to a RetryWith error, the client delays and retries the request. This configures the jitter on the retry attempted.
///
///
internal int? RandomSaltForRetryWithMilliseconds { get; set; }
///
/// Gets or sets the total time to wait before failing the request for retry with failures.
/// subscribed.
///
///
/// The default value 30 seconds.
///
///
///
/// When a request fails due to a RetryWith error, the client delays and retries the request. This configures total time spent waiting on the request.
///
///
internal int? TotalWaitTimeForRetryWithMilliseconds { get; set; }
///
/// Flag that controls whether CPU monitoring thread is created to enrich timeout exceptions with additional diagnostic. Default value is true.
///
internal bool? EnableCpuMonitor { get; set; }
///
/// Flag indicates the value of DisableServerCertificateValidation flag set at connection string level.Default it is false.
///
internal bool DisableServerCertificateValidation { get; set; }
///
/// Gets or sets Client Telemetry Options like feature flags and corresponding options
///
public CosmosClientTelemetryOptions CosmosClientTelemetryOptions { get; set; }
///
/// Create a client with Fault Injection capabilities using the Cosmos DB Fault Injection Library.
///
///
/// How to create a CosmosClient with Fault Injection capabilities.
///
/// () { rule });
///
/// CosmosClientOptions clientOptions = new CosmosClientOptions()
/// {
/// FaultInjector = faultInjector
/// };
///
/// CosmosClient client = new CosmosClient("connection string", clientOptions);
/// ]]>
///
///
public IFaultInjector FaultInjector
{
get => this.faultInjector;
set
{
this.faultInjector = value;
if (this.faultInjector != null)
{
this.ChaosInterceptorFactory = this.faultInjector.GetChaosInterceptorFactory();
}
}
}
///
/// Sets the throughput bucket for requests created using cosmos client.
///
///
/// If throughput bucket is also set at request level in , that throughput bucket is used.
/// If is set to true in CosmosClientOptions, throughput bucket can only be set at client level.
///
///
#if PREVIEW
public
#else
internal
#endif
int? ThroughputBucket { get; set; }
internal IChaosInterceptorFactory ChaosInterceptorFactory { get; set; }
internal void SetSerializerIfNotConfigured(CosmosSerializer serializer)
{
if (this.serializerInternal == null)
{
this.serializerInternal = serializer ?? throw new ArgumentNullException(nameof(serializer));
}
}
internal CosmosClientOptions Clone()
{
CosmosClientOptions cloneConfiguration = (CosmosClientOptions)this.MemberwiseClone();
return cloneConfiguration;
}
internal virtual ConnectionPolicy GetConnectionPolicy(int clientId)
{
this.ValidateDirectTCPSettings();
this.ValidateLimitToEndpointSettings();
ConnectionPolicy connectionPolicy = new ConnectionPolicy()
{
ApplicationName = this.ApplicationName,
MaxConnectionLimit = this.GatewayModeMaxConnectionLimit,
RequestTimeout = this.RequestTimeout,
ConnectionMode = this.ConnectionMode,
ConnectionProtocol = this.ConnectionProtocol,
UserAgentContainer = this.CreateUserAgentContainerWithFeatures(clientId),
UseMultipleWriteLocations = true,
IdleTcpConnectionTimeout = this.IdleTcpConnectionTimeout,
SessionRetryOptions = this.SessionRetryOptions,
OpenTcpConnectionTimeout = this.OpenTcpConnectionTimeout,
MaxRequestsPerTcpConnection = this.MaxRequestsPerTcpConnection,
MaxTcpConnectionsPerEndpoint = this.MaxTcpConnectionsPerEndpoint,
EnableEndpointDiscovery = !this.LimitToEndpoint,
EnablePartitionLevelCircuitBreaker = this.EnablePartitionLevelCircuitBreaker,
DisablePartitionLevelFailoverClientLevelOverride = this.DisablePartitionLevelFailover,
PortReuseMode = this.portReuseMode,
EnableTcpConnectionEndpointRediscovery = this.EnableTcpConnectionEndpointRediscovery,
EnableAdvancedReplicaSelectionForTcp = this.EnableAdvancedReplicaSelectionForTcp,
HttpClientFactory = this.httpClientFactory,
ServerCertificateCustomValidationCallback = this.ServerCertificateCustomValidationCallback,
CosmosClientTelemetryOptions = new CosmosClientTelemetryOptions(),
AvailabilityStrategy = this.AvailabilityStrategy,
};
if (this.CosmosClientTelemetryOptions != null)
{
connectionPolicy.CosmosClientTelemetryOptions = this.CosmosClientTelemetryOptions;
}
RegionNameMapper mapper = new RegionNameMapper();
if (!string.IsNullOrEmpty(this.ApplicationRegion))
{
connectionPolicy.SetCurrentLocation(mapper.GetCosmosDBRegionName(this.ApplicationRegion));
}
if (this.ApplicationPreferredRegions != null)
{
List mappedRegions = this.ApplicationPreferredRegions.Select(s => mapper.GetCosmosDBRegionName(s)).ToList();
connectionPolicy.SetPreferredLocations(mappedRegions);
}
if (this.AccountInitializationCustomEndpoints != null)
{
connectionPolicy.SetAccountInitializationCustomEndpoints(this.AccountInitializationCustomEndpoints);
}
if (this.MaxRetryAttemptsOnRateLimitedRequests != null)
{
connectionPolicy.RetryOptions.MaxRetryAttemptsOnThrottledRequests = this.MaxRetryAttemptsOnRateLimitedRequests.Value;
}
if (this.MaxRetryWaitTimeOnRateLimitedRequests != null)
{
connectionPolicy.RetryOptions.MaxRetryWaitTimeInSeconds = (int)this.MaxRetryWaitTimeOnRateLimitedRequests.Value.TotalSeconds;
}
if (this.InitialRetryForRetryWithMilliseconds != null)
{
connectionPolicy.RetryOptions.InitialRetryForRetryWithMilliseconds =
this.InitialRetryForRetryWithMilliseconds;
}
if (this.MaximumRetryForRetryWithMilliseconds != null)
{
connectionPolicy.RetryOptions.MaximumRetryForRetryWithMilliseconds =
this.MaximumRetryForRetryWithMilliseconds;
}
if (this.RandomSaltForRetryWithMilliseconds != null)
{
connectionPolicy.RetryOptions.RandomSaltForRetryWithMilliseconds
= this.RandomSaltForRetryWithMilliseconds;
}
if (this.TotalWaitTimeForRetryWithMilliseconds != null)
{
connectionPolicy.RetryOptions.TotalWaitTimeForRetryWithMilliseconds
= this.TotalWaitTimeForRetryWithMilliseconds;
}
return connectionPolicy;
}
internal Documents.ConsistencyLevel? GetDocumentsConsistencyLevel()
{
if (!this.ConsistencyLevel.HasValue)
{
return null;
}
return (Documents.ConsistencyLevel)this.ConsistencyLevel.Value;
}
internal static string GetAccountEndpoint(string connectionString)
{
return CosmosClientOptions.GetValueFromConnectionString(connectionString, CosmosClientOptions.ConnectionStringAccountEndpoint, null);
}
internal static string GetAccountKey(string connectionString)
{
return CosmosClientOptions.GetValueFromConnectionString(connectionString, CosmosClientOptions.ConnectionStringAccountKey, null);
}
internal static bool IsConnectionStringDisableServerCertificateValidationFlag(string connectionString)
{
return Convert.ToBoolean(CosmosClientOptions.GetValueFromConnectionString(connectionString, CosmosClientOptions.ConnectionStringDisableServerCertificateValidation, false));
}
internal static CosmosClientOptions GetCosmosClientOptionsWithCertificateFlag(string connectionString, CosmosClientOptions clientOptions)
{
clientOptions ??= new CosmosClientOptions();
if (CosmosClientOptions.IsConnectionStringDisableServerCertificateValidationFlag(connectionString))
{
clientOptions.DisableServerCertificateValidation = true;
}
return clientOptions;
}
internal bool IsCustomSerializerProvided()
{
return this.isCustomSerializerProvided;
}
private static T GetValueFromConnectionString(string connectionString, string keyName, T defaultValue)
{
if (connectionString == null)
{
throw new ArgumentNullException(nameof(connectionString));
}
DbConnectionStringBuilder builder = new DbConnectionStringBuilder { ConnectionString = connectionString };
if (builder.TryGetValue(keyName, out object value))
{
string keyNameValue = value as string;
if (!string.IsNullOrEmpty(keyNameValue))
{
try
{
return (T)Convert.ChangeType(value, typeof(T));
}
catch (InvalidCastException)
{
throw new ArgumentException("The connection string contains invalid property: " + keyName);
}
}
}
if (defaultValue != null)
{
return defaultValue;
}
throw new ArgumentException("The connection string is missing a required property: " + keyName);
}
private void ValidateLimitToEndpointSettings()
{
if (!string.IsNullOrEmpty(this.ApplicationRegion) && this.LimitToEndpoint)
{
throw new ArgumentException($"Cannot specify {nameof(this.ApplicationRegion)} and enable {nameof(this.LimitToEndpoint)}. Only one can be set.");
}
if (this.ApplicationPreferredRegions?.Count > 0 && this.LimitToEndpoint)
{
throw new ArgumentException($"Cannot specify {nameof(this.ApplicationPreferredRegions)} and enable {nameof(this.LimitToEndpoint)}. Only one can be set.");
}
if (!string.IsNullOrEmpty(this.ApplicationRegion) && this.ApplicationPreferredRegions?.Count > 0)
{
throw new ArgumentException($"Cannot specify {nameof(this.ApplicationPreferredRegions)} and {nameof(this.ApplicationRegion)}. Only one can be set.");
}
}
private void ValidateDirectTCPSettings()
{
string settingName = string.Empty;
if (this.ConnectionMode != ConnectionMode.Direct)
{
if (this.IdleTcpConnectionTimeout.HasValue)
{
settingName = nameof(this.IdleTcpConnectionTimeout);
}
else if (this.OpenTcpConnectionTimeout.HasValue)
{
settingName = nameof(this.OpenTcpConnectionTimeout);
}
else if (this.MaxRequestsPerTcpConnection.HasValue)
{
settingName = nameof(this.MaxRequestsPerTcpConnection);
}
else if (this.MaxTcpConnectionsPerEndpoint.HasValue)
{
settingName = nameof(this.MaxTcpConnectionsPerEndpoint);
}
else if (this.PortReuseMode.HasValue)
{
settingName = nameof(this.PortReuseMode);
}
}
if (!string.IsNullOrEmpty(settingName))
{
throw new ArgumentException($"{settingName} requires {nameof(this.ConnectionMode)} to be set to {nameof(ConnectionMode.Direct)}");
}
}
internal UserAgentContainer CreateUserAgentContainerWithFeatures(int clientId)
{
CosmosClientOptionsFeatures features = CosmosClientOptionsFeatures.NoFeatures;
if (this.AllowBulkExecution)
{
features |= CosmosClientOptionsFeatures.AllowBulkExecution;
}
if (this.HttpClientFactory != null)
{
features |= CosmosClientOptionsFeatures.HttpClientFactory;
}
string featureString = null;
if (features != CosmosClientOptionsFeatures.NoFeatures)
{
featureString = Convert.ToString((int)features, 2).PadLeft(8, '0');
}
string regionConfiguration = this.GetRegionConfiguration();
return new UserAgentContainer(
clientId: clientId,
features: featureString,
regionConfiguration: regionConfiguration,
suffix: this.ApplicationName);
}
///
/// This generates a key that added to the user agent to make it
/// possible to determine if the SDK has region failover enabled.
///
/// Format Reg-{D (Disabled discovery)}-S(application region)|L(List of preferred regions)|N(None, user did not configure it)
private string GetRegionConfiguration()
{
string regionConfig = this.LimitToEndpoint ? "D" : string.Empty;
if (!string.IsNullOrEmpty(this.ApplicationRegion))
{
return regionConfig + "S";
}
if (this.ApplicationPreferredRegions != null)
{
return regionConfig + "L";
}
return regionConfig + "N";
}
///
/// Serialize the current configuration into a JSON string
///
/// Returns a JSON string of the current configuration.
internal string GetSerializedConfiguration()
{
return JsonConvert.SerializeObject(this);
}
///
/// The complex object passed in by the user can contain objects that can not be serialized. Instead just log the types.
///
private class ClientOptionJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value is Collection handlers)
{
writer.WriteValue(string.Join(":", handlers.Select(x => x.GetType())));
return;
}
if (value is System.Text.Json.JsonSerializerOptions)
{
writer.WriteValue(value.GetType().ToString());
return;
}
if (value is CosmosJsonSerializerWrapper cosmosJsonSerializerWrapper)
{
writer.WriteValue(cosmosJsonSerializerWrapper.InternalJsonSerializer.GetType().ToString());
return;
}
if (value is CosmosSerializer cosmosSerializer)
{
writer.WriteValue(cosmosSerializer.GetType().ToString());
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
}
public override bool CanRead => false;
public override bool CanConvert(Type objectType)
{
return objectType == typeof(DateTime);
}
}
}
}