diff --git a/Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs b/Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs index 683ccf938f..4f89746966 100644 --- a/Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs +++ b/Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs @@ -1,286 +1,286 @@ -//------------------------------------------------------------ -// 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.Fluent; - using Microsoft.Azure.Documents; +//------------------------------------------------------------ +// 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.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"; + 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 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; - - /// - /// 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(); - } - - /// - /// 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; } - - /// - /// 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 the 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; } - - /// - /// 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. - /// - /// 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; } - - /// - /// 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; } - - /// + + private const ApiType DefaultApiType = ApiType.None; + + /// + /// Default request timeout + /// + private int gatewayModeMaxConnectionLimit; + private CosmosSerializationOptions serializerOptions; + private CosmosSerializer serializerInternal; + + 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; + + /// + /// 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(); + } + + /// + /// 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; } + + /// + /// 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 the 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; } + + /// + /// 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. + /// + /// 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; } + + /// + /// 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; } + + /// /// Sets the priority level for requests created using cosmos client. /// /// @@ -289,384 +289,384 @@ public ConnectionMode ConnectionMode /// /// 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; } - - /// - /// 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; } - - /// - /// (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. Recommended values are greater than or equal to 5 seconds. - /// - /// - /// 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. Value must be greater than or equal to 16. - /// - 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) - { - throw new ArgumentException( - $"{nameof(this.SerializerOptions)} is not compatible with {nameof(this.Serializer)}. 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) - { - throw new ArgumentException( - $"{nameof(this.Serializer)} is not compatible with {nameof(this.SerializerOptions)}. Only one can be set. "); - } - - 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. - /// - 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; - } - } - - /// - /// Enable partition key level failover - /// - internal bool EnablePartitionLevelFailover { get; set; } = ConfigurationManager.IsPartitionLevelFailoverEnabled(defaultValue: 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. - /// - /// - /// + + /// + /// 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; } + + /// + /// 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; } + + /// + /// (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. Recommended values are greater than or equal to 5 seconds. + /// + /// + /// 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. Value must be greater than or equal to 16. + /// + 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) + { + throw new ArgumentException( + $"{nameof(this.SerializerOptions)} is not compatible with {nameof(this.Serializer)}. 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) + { + throw new ArgumentException( + $"{nameof(this.Serializer)} is not compatible with {nameof(this.SerializerOptions)}. Only one can be set. "); + } + + 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. + /// + 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; + } + } + + /// + /// Enable partition key level failover + /// + internal bool EnablePartitionLevelFailover { get; set; } = ConfigurationManager.IsPartitionLevelFailoverEnabled(defaultValue: 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 this flag in production (only for emulator) - /// - /// + /// NOTE: DO NOT use the `DisableServerCertificateValidation` flag in production (only for emulator) + /// + /// public Func ServerCertificateCustomValidationCallback { get; set; } /// @@ -675,7 +675,7 @@ internal Protocol ConnectionProtocol /// /// Test hook DisableServerCertificateValidationInvocationCallback /// - When configured will invoke it when ever custom validation is done - /// + /// internal Func GetServerCertificateCustomValidationCallback() { if (this.DisableServerCertificateValidation) @@ -700,144 +700,144 @@ internal Func GetServerCerti } } - return this.ServerCertificateCustomValidationCallback; + 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; } - - /// + /// + /// 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 - /// + + /// + /// Gets or sets Client Telemetry Options like feature flags and corresponding options + /// public CosmosClientTelemetryOptions CosmosClientTelemetryOptions { 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(); - this.ValidatePartitionLevelFailoverSettings(); - - ConnectionPolicy connectionPolicy = new ConnectionPolicy() - { - MaxConnectionLimit = this.GatewayModeMaxConnectionLimit, - RequestTimeout = this.RequestTimeout, - ConnectionMode = this.ConnectionMode, - ConnectionProtocol = this.ConnectionProtocol, - UserAgentContainer = this.CreateUserAgentContainerWithFeatures(clientId), - UseMultipleWriteLocations = true, - IdleTcpConnectionTimeout = this.IdleTcpConnectionTimeout, - OpenTcpConnectionTimeout = this.OpenTcpConnectionTimeout, - MaxRequestsPerTcpConnection = this.MaxRequestsPerTcpConnection, - MaxTcpConnectionsPerEndpoint = this.MaxTcpConnectionsPerEndpoint, - EnableEndpointDiscovery = !this.LimitToEndpoint, - EnablePartitionLevelFailover = this.EnablePartitionLevelFailover, - PortReuseMode = this.portReuseMode, - EnableTcpConnectionEndpointRediscovery = this.EnableTcpConnectionEndpointRediscovery, - EnableAdvancedReplicaSelectionForTcp = this.EnableAdvancedReplicaSelectionForTcp, - HttpClientFactory = this.httpClientFactory, - ServerCertificateCustomValidationCallback = this.GetServerCertificateCustomValidationCallback(), - CosmosClientTelemetryOptions = new CosmosClientTelemetryOptions() - }; - - if (this.CosmosClientTelemetryOptions != null) - { - connectionPolicy.CosmosClientTelemetryOptions = this.CosmosClientTelemetryOptions; - } - - RegionNameMapper mapper = new RegionNameMapper(); + 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(); + this.ValidatePartitionLevelFailoverSettings(); + + ConnectionPolicy connectionPolicy = new ConnectionPolicy() + { + MaxConnectionLimit = this.GatewayModeMaxConnectionLimit, + RequestTimeout = this.RequestTimeout, + ConnectionMode = this.ConnectionMode, + ConnectionProtocol = this.ConnectionProtocol, + UserAgentContainer = this.CreateUserAgentContainerWithFeatures(clientId), + UseMultipleWriteLocations = true, + IdleTcpConnectionTimeout = this.IdleTcpConnectionTimeout, + OpenTcpConnectionTimeout = this.OpenTcpConnectionTimeout, + MaxRequestsPerTcpConnection = this.MaxRequestsPerTcpConnection, + MaxTcpConnectionsPerEndpoint = this.MaxTcpConnectionsPerEndpoint, + EnableEndpointDiscovery = !this.LimitToEndpoint, + EnablePartitionLevelFailover = this.EnablePartitionLevelFailover, + PortReuseMode = this.portReuseMode, + EnableTcpConnectionEndpointRediscovery = this.EnableTcpConnectionEndpointRediscovery, + EnableAdvancedReplicaSelectionForTcp = this.EnableAdvancedReplicaSelectionForTcp, + HttpClientFactory = this.httpClientFactory, + ServerCertificateCustomValidationCallback = this.GetServerCertificateCustomValidationCallback(), + CosmosClientTelemetryOptions = new CosmosClientTelemetryOptions() + }; + + if (this.CosmosClientTelemetryOptions != null) + { + connectionPolicy.CosmosClientTelemetryOptions = this.CosmosClientTelemetryOptions; + } + + RegionNameMapper mapper = new RegionNameMapper(); if (!string.IsNullOrEmpty(this.ApplicationRegion)) { connectionPolicy.SetCurrentLocation(mapper.GetCosmosDBRegionName(this.ApplicationRegion)); @@ -848,62 +848,62 @@ internal virtual ConnectionPolicy GetConnectionPolicy(int clientId) List mappedRegions = this.ApplicationPreferredRegions.Select(s => mapper.GetCosmosDBRegionName(s)).ToList(); connectionPolicy.SetPreferredLocations(mappedRegions); - } - - 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) - { + } + + 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) - { + } + + internal static string GetAccountKey(string connectionString) + { return CosmosClientOptions.GetValueFromConnectionString(connectionString, CosmosClientOptions.ConnectionStringAccountKey, null); } @@ -921,21 +921,21 @@ internal static CosmosClientOptions GetCosmosClientOptionsWithCertificateFlag(st } return clientOptions; - } - + } + 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)) - { + { + 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)); @@ -950,166 +950,166 @@ private static T GetValueFromConnectionString(string connectionString, string 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 ValidatePartitionLevelFailoverSettings() - { - if (this.EnablePartitionLevelFailover - && (this.ApplicationPreferredRegions == null || this.ApplicationPreferredRegions.Count == 0)) - { - throw new ArgumentException($"{nameof(this.ApplicationPreferredRegions)} is required when {nameof(this.EnablePartitionLevelFailover)} is enabled."); - } - } - - 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; - } - - CosmosJsonSerializerWrapper cosmosJsonSerializerWrapper = value as CosmosJsonSerializerWrapper; - if (value is CosmosJsonSerializerWrapper) - { - writer.WriteValue(cosmosJsonSerializerWrapper.InternalJsonSerializer.GetType().ToString()); - } - - CosmosSerializer cosmosSerializer = value as CosmosSerializer; - if (cosmosSerializer is 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); - } - } - } + } + + 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 ValidatePartitionLevelFailoverSettings() + { + if (this.EnablePartitionLevelFailover + && (this.ApplicationPreferredRegions == null || this.ApplicationPreferredRegions.Count == 0)) + { + throw new ArgumentException($"{nameof(this.ApplicationPreferredRegions)} is required when {nameof(this.EnablePartitionLevelFailover)} is enabled."); + } + } + + 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; + } + + CosmosJsonSerializerWrapper cosmosJsonSerializerWrapper = value as CosmosJsonSerializerWrapper; + if (value is CosmosJsonSerializerWrapper) + { + writer.WriteValue(cosmosJsonSerializerWrapper.InternalJsonSerializer.GetType().ToString()); + } + + CosmosSerializer cosmosSerializer = value as CosmosSerializer; + if (cosmosSerializer is 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); + } + } + } } \ No newline at end of file