Skip to content

Commit bd299f6

Browse files
Merge branch 'users/vipulvishal/pkissue' of https://github.com/Azure/azure-cosmos-dotnet-v3 into users/vipulvishal/pkissue
2 parents 0ad9672 + 4480337 commit bd299f6

23 files changed

+649
-81
lines changed

Microsoft.Azure.Cosmos/src/ClientResources.Designer.cs

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Microsoft.Azure.Cosmos/src/ClientResources.resx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,11 +268,17 @@
268268
<value>Failed to deserialize response returned by server.</value>
269269
</data>
270270
<data name="StringCompareToInvalidConstant" xml:space="preserve">
271-
<value>The right hand side of string.CompareTo() comparison must be constant '0'</value>
271+
<value>The right-hand side of String.CompareTo() comparison must be constant '0'.</value>
272+
</data>
273+
<data name="StringCompareInvalidConstant" xml:space="preserve">
274+
<value>The right-hand side of String.Compare() comparison must be constant '0'.</value>
272275
</data>
273276
<data name="StringCompareToInvalidOperator" xml:space="preserve">
274277
<value>Invalid operator for string.CompareTo(). Vaid operators are ('==', '&lt;', '&lt;=', '&gt;' or '&gt;=')</value>
275278
</data>
279+
<data name="StringCompareInvalidOperator" xml:space="preserve">
280+
<value>Invalid operator for string.Compare(). Vaid operators are ('==', '&lt;', '&lt;=', '&gt;' or '&gt;=')</value>
281+
</data>
276282
<data name="TokenRefreshInProgress" xml:space="preserve">
277283
<value>Token refresh in progress.</value>
278284
</data>

Microsoft.Azure.Cosmos/src/ConnectionPolicy.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,18 @@ public Func<HttpClient> HttpClientFactory
459459
set;
460460
}
461461

462+
/// <summary>
463+
/// Gets or sets the boolean flag to enable replica validation.
464+
/// </summary>
465+
/// <value>
466+
/// The default value for this parameter is false.
467+
/// </value>
468+
public bool? EnableAdvancedReplicaSelectionForTcp
469+
{
470+
get;
471+
set;
472+
}
473+
462474
/// <summary>
463475
/// (Direct/TCP) This is an advanced setting that controls the number of TCP connections that will be opened eagerly to each Cosmos DB back-end.
464476
/// </summary>

Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,17 @@ public ConnectionMode ConnectionMode
346346
/// <seealso cref="TransactionalBatchItemRequestOptions.EnableContentResponseOnWrite"/>
347347
public bool? EnableContentResponseOnWrite { get; set; }
348348

349+
/// <summary>
350+
/// Gets or sets the advanced replica selection flag. The advanced replica selection logic keeps track of the replica connection
351+
/// status, and based on status, it prioritizes the replicas which show healthy stable connections, so that the requests can be sent
352+
/// confidently to the particular replica. This helps the cosmos client to become more resilient and effective to any connectivity issues.
353+
/// The default value for this parameter is 'false'.
354+
/// </summary>
355+
/// <remarks>
356+
/// <para>This is optimal for latency-sensitive workloads. Does not apply if <see cref="ConnectionMode.Gateway"/> is used.</para>
357+
/// </remarks>
358+
internal bool? EnableAdvancedReplicaSelectionForTcp { get; set; }
359+
349360
/// <summary>
350361
/// (Direct/TCP) Controls the amount of idle time after which unused connections are closed.
351362
/// </summary>
@@ -758,6 +769,7 @@ internal virtual ConnectionPolicy GetConnectionPolicy(int clientId)
758769
EnablePartitionLevelFailover = this.EnablePartitionLevelFailover,
759770
PortReuseMode = this.portReuseMode,
760771
EnableTcpConnectionEndpointRediscovery = this.EnableTcpConnectionEndpointRediscovery,
772+
EnableAdvancedReplicaSelectionForTcp = this.EnableAdvancedReplicaSelectionForTcp,
761773
HttpClientFactory = this.httpClientFactory,
762774
ServerCertificateCustomValidationCallback = this.ServerCertificateCustomValidationCallback
763775
};

Microsoft.Azure.Cosmos/src/DocumentClient.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ public DocumentClient(Uri serviceEndpoint,
233233

234234
this.Initialize(serviceEndpoint, connectionPolicy, desiredConsistencyLevel);
235235
this.initTaskCache = new AsyncCacheNonBlocking<string, bool>(cancellationToken: this.cancellationTokenSource.Token);
236-
this.isReplicaAddressValidationEnabled = ConfigurationManager.IsReplicaAddressValidationEnabled();
236+
this.isReplicaAddressValidationEnabled = ConfigurationManager.IsReplicaAddressValidationEnabled(connectionPolicy);
237237
}
238238

239239
/// <summary>

Microsoft.Azure.Cosmos/src/Linq/ExpressionToSQL.cs

Lines changed: 75 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,11 @@ private static SqlScalarExpression VisitBinary(BinaryExpression inputExpression,
457457
{
458458
return ExpressionToSql.VisitStringCompareTo(methodCallExpression, constantExpression, inputExpression.NodeType, reverseNodeType, context);
459459
}
460+
461+
if (TryMatchStringCompare(methodCallExpression, constantExpression, inputExpression.NodeType))
462+
{
463+
return ExpressionToSql.VisitStringCompare(methodCallExpression, constantExpression, inputExpression.NodeType, reverseNodeType, context);
464+
}
460465
}
461466

462467
SqlScalarExpression left = ExpressionToSql.VisitScalarExpression(inputExpression.Left, context);
@@ -615,32 +620,90 @@ private static SqlScalarExpression VisitStringCompareTo(
615620
{
616621
if (reverseNodeType)
617622
{
623+
compareOperator = ReverseExpressionTypeForStrings(compareOperator, ClientResources.StringCompareToInvalidOperator);
624+
}
625+
626+
SqlBinaryScalarOperatorKind op = GetBinaryOperatorKind(compareOperator, null);
627+
628+
SqlScalarExpression leftExpression = ExpressionToSql.VisitNonSubqueryScalarExpression(left.Object, context);
629+
SqlScalarExpression rightExpression = ExpressionToSql.VisitNonSubqueryScalarExpression(left.Arguments[0], context);
630+
631+
return SqlBinaryScalarExpression.Create(op, leftExpression, rightExpression);
632+
}
633+
634+
private static ExpressionType ReverseExpressionTypeForStrings(ExpressionType compareOperator, string errorMessage)
635+
{
636+
switch (compareOperator)
637+
{
638+
case ExpressionType.Equal:
639+
// do nothing
640+
break;
641+
case ExpressionType.GreaterThan:
642+
compareOperator = ExpressionType.LessThan;
643+
break;
644+
case ExpressionType.GreaterThanOrEqual:
645+
compareOperator = ExpressionType.LessThanOrEqual;
646+
break;
647+
case ExpressionType.LessThan:
648+
compareOperator = ExpressionType.GreaterThan;
649+
break;
650+
case ExpressionType.LessThanOrEqual:
651+
compareOperator = ExpressionType.GreaterThanOrEqual;
652+
break;
653+
default:
654+
throw new DocumentQueryException(string.Format(CultureInfo.CurrentCulture, errorMessage));
655+
}
656+
657+
return compareOperator;
658+
}
659+
660+
private static bool TryMatchStringCompare(MethodCallExpression left, ConstantExpression right, ExpressionType compareOperator)
661+
{
662+
if (left.Method.Equals(typeof(string).GetMethod("Compare", new Type[] { typeof(string), typeof(string) })) && left.Arguments.Count == 2)
663+
{
664+
// operator can only be =, >, >=, <, <=
618665
switch (compareOperator)
619666
{
620667
case ExpressionType.Equal:
621-
// do nothing
622-
break;
623668
case ExpressionType.GreaterThan:
624-
compareOperator = ExpressionType.LessThan;
625-
break;
626669
case ExpressionType.GreaterThanOrEqual:
627-
compareOperator = ExpressionType.LessThanOrEqual;
628-
break;
629670
case ExpressionType.LessThan:
630-
compareOperator = ExpressionType.GreaterThan;
631-
break;
632671
case ExpressionType.LessThanOrEqual:
633-
compareOperator = ExpressionType.GreaterThanOrEqual;
634672
break;
635673
default:
636-
throw new DocumentQueryException(string.Format(CultureInfo.CurrentCulture, ClientResources.StringCompareToInvalidOperator));
674+
throw new DocumentQueryException(string.Format(CultureInfo.CurrentCulture, ClientResources.StringCompareInvalidOperator));
675+
}
676+
677+
// the constant value should be zero, otherwise we can't determine how to translate the expression
678+
// it could be either integer or nullable integer
679+
if (!(right.Type == typeof(int) && (int)right.Value == 0) &&
680+
!(right.Type == typeof(int?) && ((int?)right.Value).HasValue && ((int?)right.Value).Value == 0))
681+
{
682+
throw new DocumentQueryException(string.Format(CultureInfo.CurrentCulture, ClientResources.StringCompareInvalidConstant));
637683
}
684+
685+
return true;
686+
}
687+
688+
return false;
689+
}
690+
691+
private static SqlScalarExpression VisitStringCompare(
692+
MethodCallExpression left,
693+
ConstantExpression right,
694+
ExpressionType compareOperator,
695+
bool reverseNodeType,
696+
TranslationContext context)
697+
{
698+
if (reverseNodeType)
699+
{
700+
compareOperator = ReverseExpressionTypeForStrings(compareOperator, ClientResources.StringCompareInvalidOperator);
638701
}
639702

640703
SqlBinaryScalarOperatorKind op = GetBinaryOperatorKind(compareOperator, null);
641704

642-
SqlScalarExpression leftExpression = ExpressionToSql.VisitNonSubqueryScalarExpression(left.Object, context);
643-
SqlScalarExpression rightExpression = ExpressionToSql.VisitNonSubqueryScalarExpression(left.Arguments[0], context);
705+
SqlScalarExpression leftExpression = ExpressionToSql.VisitNonSubqueryScalarExpression(left.Arguments[0], context);
706+
SqlScalarExpression rightExpression = ExpressionToSql.VisitNonSubqueryScalarExpression(left.Arguments[1], context);
644707

645708
return SqlBinaryScalarExpression.Create(op, leftExpression, rightExpression);
646709
}

Microsoft.Azure.Cosmos/src/ReadFeed/ReadFeedIteratorCore.cs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,16 @@ namespace Microsoft.Azure.Cosmos.ReadFeed
99
using System.Threading;
1010
using System.Threading.Tasks;
1111
using Microsoft.Azure.Cosmos.CosmosElements;
12-
using Microsoft.Azure.Cosmos.Diagnostics;
12+
using Microsoft.Azure.Cosmos.Json;
1313
using Microsoft.Azure.Cosmos.Pagination;
1414
using Microsoft.Azure.Cosmos.Query.Core;
15+
using Microsoft.Azure.Cosmos.Query.Core.Exceptions;
1516
using Microsoft.Azure.Cosmos.Query.Core.Monads;
1617
using Microsoft.Azure.Cosmos.ReadFeed.Pagination;
18+
using Microsoft.Azure.Cosmos.Resource.CosmosExceptions;
1719
using Microsoft.Azure.Cosmos.Routing;
1820
using Microsoft.Azure.Cosmos.Tracing;
21+
using Microsoft.Azure.Documents;
1922

2023
/// <summary>
2124
/// Cosmos feed stream iterator. This is used to get the query responses with a Stream content
@@ -113,7 +116,25 @@ public ReadFeedIteratorCore(
113116
else
114117
{
115118
CosmosString tokenAsString = (CosmosString)token;
116-
state = ReadFeedState.Continuation(CosmosElement.Parse(tokenAsString.Value));
119+
try
120+
{
121+
state = ReadFeedState.Continuation(CosmosElement.Parse(tokenAsString.Value));
122+
}
123+
catch (Exception exception) when (exception.InnerException is JsonParseException)
124+
{
125+
MalformedContinuationTokenException malformedContinuationTokenException = new MalformedContinuationTokenException(exception.Message);
126+
throw CosmosExceptionFactory.CreateBadRequestException(
127+
message: $"Malformed Continuation Token: {tokenAsString}.",
128+
headers: CosmosQueryResponseMessageHeaders.ConvertToQueryHeaders(
129+
new Headers(),
130+
default,
131+
default,
132+
(int)SubStatusCodes.MalformedContinuationToken,
133+
default),
134+
stackTrace: exception.StackTrace,
135+
innerException: malformedContinuationTokenException,
136+
trace: null);
137+
}
117138
}
118139

119140
FeedRangeState<ReadFeedState> feedRangeState = new FeedRangeState<ReadFeedState>(feedRange, state);

Microsoft.Azure.Cosmos/src/ResourceThrottleRetryPolicy.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public Task<ShouldRetryResult> ShouldRetryAsync(
5252
{
5353
if (!this.IsValidThrottleStatusCode(dce.StatusCode))
5454
{
55-
DefaultTrace.TraceError(
55+
DefaultTrace.TraceVerbose(
5656
"Operation will NOT be retried. Current attempt {0}, Status Code: {1} ",
5757
this.currentAttemptCount,
5858
dce.StatusCode);
@@ -81,7 +81,7 @@ public Task<ShouldRetryResult> ShouldRetryAsync(
8181
{
8282
if (!this.IsValidThrottleStatusCode(cosmosResponseMessage?.StatusCode))
8383
{
84-
DefaultTrace.TraceError(
84+
DefaultTrace.TraceVerbose(
8585
"Operation will NOT be retried. Current attempt {0}, Status Code: {1} ",
8686
this.currentAttemptCount,
8787
cosmosResponseMessage?.StatusCode);

Microsoft.Azure.Cosmos/src/Routing/GlobalAddressResolver.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public GlobalAddressResolver(
6767

6868
this.enableTcpConnectionEndpointRediscovery = connectionPolicy.EnableTcpConnectionEndpointRediscovery;
6969

70-
this.isReplicaAddressValidationEnabled = ConfigurationManager.IsReplicaAddressValidationEnabled();
70+
this.isReplicaAddressValidationEnabled = ConfigurationManager.IsReplicaAddressValidationEnabled(connectionPolicy);
7171

7272
this.maxEndpoints = maxBackupReadEndpoints + 2; // for write and alternate write endpoint (during failover)
7373

Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/CosmosDbEventSource.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,12 @@ public static void RecordDiagnosticsForRequests(
3535
Documents.OperationType operationType,
3636
OpenTelemetryAttributes response)
3737
{
38-
if (DiagnosticsFilterHelper.IsTracingNeeded(
38+
if (!DiagnosticsFilterHelper.IsSuccessfulResponse(
39+
response: response) && CosmosDbEventSource.IsEnabled(EventLevel.Warning))
40+
{
41+
CosmosDbEventSource.Singleton.FailedRequest(response.Diagnostics.ToString());
42+
}
43+
else if (DiagnosticsFilterHelper.IsLatencyThresholdCrossed(
3944
config: config,
4045
operationType: operationType,
4146
response: response) && CosmosDbEventSource.IsEnabled(EventLevel.Warning))
@@ -64,5 +69,11 @@ private void LatencyOverThreshold(string message)
6469
{
6570
this.WriteEvent(2, message);
6671
}
72+
73+
[Event(3, Level = EventLevel.Error)]
74+
private void FailedRequest(string message)
75+
{
76+
this.WriteEvent(3, message);
77+
}
6778
}
6879
}

0 commit comments

Comments
 (0)