Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 47 additions & 5 deletions Microsoft.Azure.Cosmos/src/ConnectionPolicy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,17 @@ internal sealed class ConnectionPolicy
private const int defaultMediaRequestTimeout = 300;
private const int defaultMaxConcurrentFanoutRequests = 32;
private const int defaultMaxConcurrentConnectionLimit = 50;
private const int minimumMaxRequestsPerTcpConnection = 4;
private const int minimumMaxTcpConnectionsPerEndpoint = 16;

internal UserAgentContainer UserAgentContainer;
private static ConnectionPolicy defaultPolicy;

private Protocol connectionProtocol;
private ObservableCollection<string> preferredLocations;
private ObservableCollection<Uri> accountInitializationCustomEndpoints;
private int? maxRequestsPerTcpConnection;
private int? maxTcpConnectionsPerEndpoint;

/// <summary>
/// Initializes a new instance of the <see cref="ConnectionPolicy"/> class to connect to the Azure Cosmos DB service.
Expand Down Expand Up @@ -468,8 +472,20 @@ public TimeSpan? OpenTcpConnectionTimeout
/// </remarks>
public int? MaxRequestsPerTcpConnection
{
get;
set;
get
{
return this.maxRequestsPerTcpConnection;
}

set
{
ConnectionPolicy.ValidateDirectTcpConnectionLimit(
value,
ConnectionPolicy.minimumMaxRequestsPerTcpConnection,
nameof(this.MaxRequestsPerTcpConnection));

this.maxRequestsPerTcpConnection = value;
}
}

/// <summary>
Expand All @@ -481,8 +497,20 @@ public int? MaxRequestsPerTcpConnection
/// </value>
public int? MaxTcpConnectionsPerEndpoint
{
get;
set;
get
{
return this.maxTcpConnectionsPerEndpoint;
}

set
{
ConnectionPolicy.ValidateDirectTcpConnectionLimit(
value,
ConnectionPolicy.minimumMaxTcpConnectionsPerEndpoint,
nameof(this.MaxTcpConnectionsPerEndpoint));

this.maxTcpConnectionsPerEndpoint = value;
}
}

/// <summary>
Expand Down Expand Up @@ -607,5 +635,19 @@ internal RetryWithConfiguration GetRetryWithConfiguration()
{
return this.RetryOptions?.GetRetryWithConfiguration();
}

private static void ValidateDirectTcpConnectionLimit(
int? value,
int minimumValue,
string settingName)
{
if (value.HasValue && value.Value < minimumValue)
{
throw new ArgumentOutOfRangeException(
settingName,
value.Value,
$"{settingName} must be greater than or equal to {minimumValue}.");
}
}
}
}
}
41 changes: 41 additions & 0 deletions Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ public class CosmosClientOptions
/// Default Protocol mode
/// </summary>
private const Protocol DefaultProtocol = Protocol.Tcp;
private const int MinimumMaxRequestsPerTcpConnection = 4;
private const int MinimumMaxTcpConnectionsPerEndpoint = 16;

private const string ConnectionStringAccountEndpoint = "AccountEndpoint";
private const string ConnectionStringAccountKey = "AccountKey";
Expand Down Expand Up @@ -621,6 +623,14 @@ public int? MaxRequestsPerTcpConnection
get => this.maxRequestsPerTcpConnection;
set
{
if (this.ConnectionMode == ConnectionMode.Direct)
{
CosmosClientOptions.ValidateDirectTcpConnectionLimit(
value,
CosmosClientOptions.MinimumMaxRequestsPerTcpConnection,
nameof(this.MaxRequestsPerTcpConnection));
}

this.maxRequestsPerTcpConnection = value;
this.ValidateDirectTCPSettings();
}
Expand All @@ -638,6 +648,14 @@ public int? MaxTcpConnectionsPerEndpoint
get => this.maxTcpConnectionsPerEndpoint;
set
{
if (this.ConnectionMode == ConnectionMode.Direct)
{
CosmosClientOptions.ValidateDirectTcpConnectionLimit(
value,
CosmosClientOptions.MinimumMaxTcpConnectionsPerEndpoint,
nameof(this.MaxTcpConnectionsPerEndpoint));
}

this.maxTcpConnectionsPerEndpoint = value;
this.ValidateDirectTCPSettings();
}
Expand Down Expand Up @@ -1329,6 +1347,29 @@ private void ValidateDirectTCPSettings()
{
throw new ArgumentException($"{settingName} requires {nameof(this.ConnectionMode)} to be set to {nameof(ConnectionMode.Direct)}");
}

CosmosClientOptions.ValidateDirectTcpConnectionLimit(
this.MaxRequestsPerTcpConnection,
CosmosClientOptions.MinimumMaxRequestsPerTcpConnection,
nameof(this.MaxRequestsPerTcpConnection));
CosmosClientOptions.ValidateDirectTcpConnectionLimit(
this.MaxTcpConnectionsPerEndpoint,
CosmosClientOptions.MinimumMaxTcpConnectionsPerEndpoint,
nameof(this.MaxTcpConnectionsPerEndpoint));
}

private static void ValidateDirectTcpConnectionLimit(
int? value,
int minimumValue,
string settingName)
{
if (value.HasValue && value.Value < minimumValue)
{
throw new ArgumentOutOfRangeException(
settingName,
value.Value,
$"{settingName} must be greater than or equal to {minimumValue}.");
}
}

internal UserAgentContainer CreateUserAgentContainerWithFeatures(int clientId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -809,11 +809,11 @@ public void ThrowOnMissingAccountEndpointInConnectionString()
}

[TestMethod]
public void VerifyGetConnectionPolicyThrowIfDirectTcpSettingAreUsedInGatewayMode()
{
TimeSpan idleTcpConnectionTimeout = new TimeSpan(0, 10, 0);
TimeSpan openTcpConnectionTimeout = new TimeSpan(0, 0, 5);
int maxRequestsPerTcpConnection = 30;
public void VerifyGetConnectionPolicyThrowIfDirectTcpSettingAreUsedInGatewayMode()
{
TimeSpan idleTcpConnectionTimeout = new TimeSpan(0, 10, 0);
TimeSpan openTcpConnectionTimeout = new TimeSpan(0, 0, 5);
int maxRequestsPerTcpConnection = 30;
int maxTcpConnectionsPerEndpoint = 65535;

CosmosClientOptions cosmosClientOptions = new CosmosClientOptions()
Expand All @@ -824,14 +824,69 @@ public void VerifyGetConnectionPolicyThrowIfDirectTcpSettingAreUsedInGatewayMode
Assert.ThrowsException<ArgumentException>(() => cosmosClientOptions.IdleTcpConnectionTimeout = idleTcpConnectionTimeout);
Assert.ThrowsException<ArgumentException>(() => cosmosClientOptions.OpenTcpConnectionTimeout = openTcpConnectionTimeout);
Assert.ThrowsException<ArgumentException>(() => cosmosClientOptions.MaxRequestsPerTcpConnection = maxRequestsPerTcpConnection);
Assert.ThrowsException<ArgumentException>(() => cosmosClientOptions.MaxTcpConnectionsPerEndpoint = maxTcpConnectionsPerEndpoint);
}

[TestMethod]
public void VerifyHttpClientFactoryBlockedWithConnectionLimit()
{
CosmosClientOptions cosmosClientOptions = new CosmosClientOptions()
{
Assert.ThrowsException<ArgumentException>(() => cosmosClientOptions.MaxTcpConnectionsPerEndpoint = maxTcpConnectionsPerEndpoint);
}

[TestMethod]
public void VerifyCosmosClientOptionsDirectTcpConnectionLimitsValidateMinimumValues()
{
CosmosClientOptions cosmosClientOptions = new CosmosClientOptions()
{
ConnectionMode = ConnectionMode.Direct
};

cosmosClientOptions.MaxTcpConnectionsPerEndpoint = null;
Assert.IsNull(cosmosClientOptions.MaxTcpConnectionsPerEndpoint);

cosmosClientOptions.MaxTcpConnectionsPerEndpoint = 16;
Assert.AreEqual(16, cosmosClientOptions.MaxTcpConnectionsPerEndpoint);

Assert.ThrowsException<ArgumentOutOfRangeException>(() => cosmosClientOptions.MaxTcpConnectionsPerEndpoint = 15);
Assert.ThrowsException<ArgumentOutOfRangeException>(() => cosmosClientOptions.MaxTcpConnectionsPerEndpoint = 0);
Assert.ThrowsException<ArgumentOutOfRangeException>(() => cosmosClientOptions.MaxTcpConnectionsPerEndpoint = -1);

cosmosClientOptions.MaxRequestsPerTcpConnection = null;
Assert.IsNull(cosmosClientOptions.MaxRequestsPerTcpConnection);

cosmosClientOptions.MaxRequestsPerTcpConnection = 4;
Assert.AreEqual(4, cosmosClientOptions.MaxRequestsPerTcpConnection);

Assert.ThrowsException<ArgumentOutOfRangeException>(() => cosmosClientOptions.MaxRequestsPerTcpConnection = 3);
Assert.ThrowsException<ArgumentOutOfRangeException>(() => cosmosClientOptions.MaxRequestsPerTcpConnection = 0);
Assert.ThrowsException<ArgumentOutOfRangeException>(() => cosmosClientOptions.MaxRequestsPerTcpConnection = -1);
}

[TestMethod]
public void VerifyConnectionPolicyDirectTcpConnectionLimitsValidateMinimumValues()
{
ConnectionPolicy connectionPolicy = new ConnectionPolicy();

connectionPolicy.MaxTcpConnectionsPerEndpoint = null;
Assert.IsNull(connectionPolicy.MaxTcpConnectionsPerEndpoint);

connectionPolicy.MaxTcpConnectionsPerEndpoint = 16;
Assert.AreEqual(16, connectionPolicy.MaxTcpConnectionsPerEndpoint);

Assert.ThrowsException<ArgumentOutOfRangeException>(() => connectionPolicy.MaxTcpConnectionsPerEndpoint = 15);
Assert.ThrowsException<ArgumentOutOfRangeException>(() => connectionPolicy.MaxTcpConnectionsPerEndpoint = 0);
Assert.ThrowsException<ArgumentOutOfRangeException>(() => connectionPolicy.MaxTcpConnectionsPerEndpoint = -1);

connectionPolicy.MaxRequestsPerTcpConnection = null;
Assert.IsNull(connectionPolicy.MaxRequestsPerTcpConnection);

connectionPolicy.MaxRequestsPerTcpConnection = 4;
Assert.AreEqual(4, connectionPolicy.MaxRequestsPerTcpConnection);

Assert.ThrowsException<ArgumentOutOfRangeException>(() => connectionPolicy.MaxRequestsPerTcpConnection = 3);
Assert.ThrowsException<ArgumentOutOfRangeException>(() => connectionPolicy.MaxRequestsPerTcpConnection = 0);
Assert.ThrowsException<ArgumentOutOfRangeException>(() => connectionPolicy.MaxRequestsPerTcpConnection = -1);
}

[TestMethod]
public void VerifyHttpClientFactoryBlockedWithConnectionLimit()
{
CosmosClientOptions cosmosClientOptions = new CosmosClientOptions()
{
GatewayModeMaxConnectionLimit = 42
};

Expand Down Expand Up @@ -1336,4 +1391,4 @@ public int Compare(object x, object y)
}
}
}
}
}
2 changes: 2 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

#### Bugs Fixed

- Fixes Direct/TCP connection limit options to reject values below their documented minimums. See [PR 5883](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5883).

#### Other Changes

### <a name="3.61.0-preview.0"/> [3.61.0-preview.0](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.61.0-preview.0) - 2026-5-18
Expand Down