Skip to content

Commit 79a11bf

Browse files
ananth7592dibahlfikirankumarkollikundadebdatta
authored
3.52.1: Adds Patch release 3.52.1 (#5293)
## Description Includes the following commit in the patch version release besides the required file changes for version upgrade to 3.52.1 QueryPlan : Fixes 410 Gone Exception on non-x64 platforms #5257 ## Type of change Please delete options that are not relevant. - [] Bug fix (non-breaking change which fixes an issue) - [] New feature (non-breaking change which adds functionality) - [] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [] This change requires a documentation update ## Closing issues To automatically close an issue: closes #IssueNumber --------- Co-authored-by: dibahlfi <106994927+dibahlfi@users.noreply.github.com> Co-authored-by: Kiran Kumar Kolli <kirankk@microsoft.com> Co-authored-by: Debdatta Kunda <87335885+kundadebdatta@users.noreply.github.com>
1 parent 16558bb commit 79a11bf

7 files changed

Lines changed: 3845 additions & 23 deletions

File tree

Directory.Build.props

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
22
<PropertyGroup>
3-
<ClientOfficialVersion>3.52.0</ClientOfficialVersion>
3+
<ClientOfficialVersion>3.52.1</ClientOfficialVersion>
44
<ClientPreviewVersion>3.53.0</ClientPreviewVersion>
5-
<ClientPreviewSuffixVersion>preview.0</ClientPreviewSuffixVersion>
5+
<ClientPreviewSuffixVersion>preview.1</ClientPreviewSuffixVersion>
66
<DirectVersion>3.39.1</DirectVersion>
77
<FaultInjectionVersion>1.0.0</FaultInjectionVersion>
88
<FaultInjectionSuffixVersion>beta.0</FaultInjectionSuffixVersion>

Microsoft.Azure.Cosmos/contracts/API_3.52.1.txt

Lines changed: 1756 additions & 0 deletions
Large diffs are not rendered by default.

Microsoft.Azure.Cosmos/contracts/API_3.53.0-preview.1.txt

Lines changed: 1890 additions & 0 deletions
Large diffs are not rendered by default.

Microsoft.Azure.Cosmos/src/Handler/RequestInvokerHandler.cs

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -305,17 +305,22 @@ public virtual async Task<ResponseMessage> SendAsync(
305305
// For epk range filtering we can end up in one of 3 cases:
306306
if (overlappingRanges.Count > 1)
307307
{
308-
// 1) The EpkRange spans more than one physical partition
309-
// In this case it means we have encountered a split and
310-
// we need to bubble that up to the higher layers to update their datastructures
311-
CosmosException goneException = new CosmosException(
312-
message: $"Epk Range: {feedRangeEpk.Range} is gone.",
313-
statusCode: System.Net.HttpStatusCode.Gone,
314-
subStatusCode: (int)SubStatusCodes.PartitionKeyRangeGone,
315-
activityId: Guid.NewGuid().ToString(),
316-
requestCharge: default);
317-
318-
return goneException.ToCosmosResponseMessage(request);
308+
//If we are running a query plan and our provided partition key results in a hash that resolves to more than one EPKRanges then its a valid use case
309+
bool isQueryPlanOperation = request.ResourceType == ResourceType.Document && request.OperationType == OperationType.QueryPlan;
310+
if (!isQueryPlanOperation)
311+
{
312+
// 1) The EpkRange spans more than one physical partition
313+
// In this case it means we have encountered a split and
314+
// we need to bubble that up to the higher layers to update their datastructures
315+
CosmosException goneException = new CosmosException(
316+
message: $"Epk Range: {feedRangeEpk.Range} is gone.",
317+
statusCode: System.Net.HttpStatusCode.Gone,
318+
subStatusCode: (int)SubStatusCodes.PartitionKeyRangeGone,
319+
activityId: Guid.NewGuid().ToString(),
320+
requestCharge: default);
321+
322+
return goneException.ToCosmosResponseMessage(request);
323+
}
319324
}
320325
// overlappingRanges.Count == 1
321326
else

Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemIntegrationTests.cs

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,16 @@
55
using System.IO;
66
using System.Linq;
77
using System.Net;
8+
using System.Net.Http;
89
using System.Text;
910
using System.Text.Json;
1011
using System.Text.Json.Serialization;
1112
using System.Threading;
1213
using System.Threading.Tasks;
1314
using Microsoft.Azure.Cosmos.Diagnostics;
1415
using Microsoft.Azure.Cosmos.FaultInjection;
15-
using Microsoft.VisualStudio.TestTools.UnitTesting;
16+
using Microsoft.VisualStudio.TestTools.UnitTesting;
17+
using Newtonsoft.Json.Linq;
1618
using static Microsoft.Azure.Cosmos.Routing.GlobalPartitionEndpointManagerCore;
1719
using static Microsoft.Azure.Cosmos.SDK.EmulatorTests.MultiRegionSetupHelpers;
1820

@@ -1327,19 +1329,14 @@ public async Task CreateAndReadItemAsync_WithCircuitBreakerEnabledAndMultiMaster
13271329

13281330
[TestMethod]
13291331
[Owner("dkunda")]
1330-
[TestCategory("MultiRegion")]
1332+
[TestCategory("MultiRegion")]
13311333
[Timeout(70000)]
13321334
[DataRow(true, DisplayName = "Test scenario when PPAF is enabled at client level.")]
13331335
[DataRow(false, DisplayName = "Test scenario when PPAF is disabled at client level.")]
13341336
public async Task ReadItemAsync_WithPPAFEnabledAndSingleMasterAccountWithResponseDelay_ShouldHedgeRequestToMultipleRegions(
13351337
bool enablePartitionLevelFailover)
13361338
{
13371339
// Arrange.
1338-
if (enablePartitionLevelFailover)
1339-
{
1340-
Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelFailoverEnabled, "True");
1341-
}
1342-
13431340
// Enabling fault injection rule to simulate a 503 service unavailable scenario.
13441341
string serviceUnavailableRuleId = "503-rule-" + Guid.NewGuid().ToString();
13451342
FaultInjectionRule serviceUnavailableRule = new FaultInjectionRuleBuilder(
@@ -1356,15 +1353,44 @@ public async Task ReadItemAsync_WithPPAFEnabledAndSingleMasterAccountWithRespons
13561353
.Build();
13571354

13581355
List<FaultInjectionRule> rules = new List<FaultInjectionRule> { serviceUnavailableRule };
1359-
FaultInjector faultInjector = new FaultInjector(rules);
1356+
FaultInjector faultInjector = new FaultInjector(rules);
1357+
1358+
// Now that the ppaf enablement flag is returned from gateway, we need to intercept the response and remove the flag from the response, so that
1359+
// the environment variable set above is honored.
1360+
HttpClientHandlerHelper httpClientHandlerHelper = new HttpClientHandlerHelper()
1361+
{
1362+
ResponseIntercepter = async (response, request) =>
1363+
{
1364+
string json = await response?.Content?.ReadAsStringAsync();
1365+
if (json.Length > 0 && json.Contains("enablePerPartitionFailoverBehavior"))
1366+
{
1367+
JObject parsedDatabaseAccountResponse = JObject.Parse(json);
1368+
parsedDatabaseAccountResponse.Property("enablePerPartitionFailoverBehavior").Value = enablePartitionLevelFailover.ToString();
1369+
1370+
HttpResponseMessage interceptedResponse = new()
1371+
{
1372+
StatusCode = response.StatusCode,
1373+
Content = new StringContent(parsedDatabaseAccountResponse.ToString()),
1374+
Version = response.Version,
1375+
ReasonPhrase = response.ReasonPhrase,
1376+
RequestMessage = response.RequestMessage,
1377+
};
1378+
1379+
return interceptedResponse;
1380+
}
1381+
1382+
return response;
1383+
},
1384+
};
13601385

13611386
List<string> preferredRegions = new List<string> { region1, region2, region3 };
13621387
CosmosClientOptions cosmosClientOptions = new CosmosClientOptions()
13631388
{
13641389
ConsistencyLevel = ConsistencyLevel.Session,
13651390
FaultInjector = faultInjector,
13661391
RequestTimeout = TimeSpan.FromSeconds(5),
1367-
ApplicationPreferredRegions = preferredRegions,
1392+
ApplicationPreferredRegions = preferredRegions,
1393+
HttpClientFactory = () => new HttpClient(httpClientHandlerHelper),
13681394
};
13691395

13701396
List<CosmosIntegrationTestObject> itemsList = new()
@@ -1386,7 +1412,7 @@ public async Task ReadItemAsync_WithPPAFEnabledAndSingleMasterAccountWithRespons
13861412

13871413
ItemResponse<CosmosIntegrationTestObject> readResponse = await container.ReadItemAsync<CosmosIntegrationTestObject>(
13881414
id: itemsList[0].Id,
1389-
partitionKey: new PartitionKey(itemsList[0].Pk));
1415+
partitionKey: new PartitionKey(itemsList[0].Pk));
13901416

13911417
IReadOnlyList<(string regionName, Uri uri)> contactedRegionMapping = readResponse.Diagnostics.GetContactedRegions();
13921418
HashSet<string> contactedRegions = new(contactedRegionMapping.Select(r => r.regionName));

Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/RetryHandlerTests.cs

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,16 @@
55
namespace Microsoft.Azure.Cosmos.Tests
66
{
77
using System;
8+
using System.Collections.Generic;
9+
using System.IO;
810
using System.Net;
911
using System.Net.Http;
1012
using System.Threading;
1113
using System.Threading.Tasks;
14+
using global::Azure;
1215
using Microsoft.Azure.Cosmos.Handlers;
16+
using Microsoft.Azure.Cosmos.Routing;
17+
using Microsoft.Azure.Cosmos.Tracing;
1318
using Microsoft.Azure.Documents;
1419
using Microsoft.VisualStudio.TestTools.UnitTesting;
1520
using Moq;
@@ -18,6 +23,139 @@ namespace Microsoft.Azure.Cosmos.Tests
1823
public class RetryHandlerTests
1924
{
2025
private static readonly Uri TestUri = new Uri("https://dummy.documents.azure.com:443/dbs");
26+
[TestMethod]
27+
public async Task ValidateQueryPlanDoesNotThrowExceptionForOverlappingRanges()
28+
{
29+
await this.ValidateOverlappingRangesBehaviorAsync(
30+
operationType: OperationType.QueryPlan,
31+
shouldThrowGoneException: false);
32+
}
33+
34+
[TestMethod]
35+
public async Task ValidateQueryThrowsGoneExceptionForOverlappingRanges()
36+
{
37+
await this.ValidateOverlappingRangesBehaviorAsync(
38+
operationType: OperationType.Query,
39+
shouldThrowGoneException: true);
40+
}
41+
42+
private async Task ValidateOverlappingRangesBehaviorAsync(
43+
OperationType operationType,
44+
bool shouldThrowGoneException)
45+
{
46+
// Create overlapping ranges for the test
47+
List<PartitionKeyRange> overlappingRanges = new List<PartitionKeyRange>
48+
{
49+
new PartitionKeyRange { Id = "0", MinInclusive = "0D4DC2CD8F49C65A8E0C5306B61B4343", MaxExclusive = "0DCEB8CE51C6BFE84F4BD9409F69B9BB2164DEBD78C50C850E0C1E3E3F0579ED" },
50+
new PartitionKeyRange { Id = "1", MinInclusive = "0DCEB8CE51C6BFE84F4BD9409F69B9BB2164DEBD78C50C850E0C1E3E3F0579ED", MaxExclusive = "1080F600C27CF98DC13F8639E94E7676" }
51+
};
52+
53+
// Create a custom document client with our TestPartitionKeyRangeCache
54+
var testPartitionKeyRangeCache = new TestPartitionKeyRangeCache(overlappingRanges);
55+
var customDocClient = new CustomMockDocumentClient(testPartitionKeyRangeCache);
56+
57+
// Create CosmosClient with our custom document client
58+
using CosmosClient client = new CosmosClient(
59+
"https://localhost:8081",
60+
MockCosmosUtil.RandomInvalidCorrectlyFormatedAuthKey,
61+
new CosmosClientOptions(),
62+
customDocClient);
63+
64+
// Create mock container
65+
Mock<ContainerInternal> containerMock = MockCosmosUtil.CreateMockContainer("testDb", "testColl");
66+
67+
// Setup container properties
68+
ContainerProperties containerProps = new ContainerProperties("testColl", "/pk");
69+
var resourceIdProperty = typeof(ContainerProperties).GetProperty(
70+
"ResourceId",
71+
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
72+
resourceIdProperty.SetValue(containerProps, "testCollRid");
73+
74+
// Set up additional mocks as needed
75+
containerMock.Setup(c => c.GetCachedContainerPropertiesAsync(
76+
It.IsAny<bool>(), It.IsAny<ITrace>(), It.IsAny<CancellationToken>()))
77+
.ReturnsAsync(containerProps);
78+
79+
Mock<Cosmos.Database> databaseMock = new Mock<Cosmos.Database>();
80+
databaseMock.Setup(d => d.Id).Returns("testDb");
81+
containerMock.Setup(c => c.Database).Returns(databaseMock.Object);
82+
83+
// FeedRangeEpk for the test - use a range that overlaps both partition key ranges
84+
FeedRangeEpk feedRange = new FeedRangeEpk(new Documents.Routing.Range<string>(
85+
"0DCEB8CE51C6BFE84F4BD9409F69B9BB",
86+
"0DCEB8CE51C6BFE84F4BD9409F69B9BBFF",
87+
true, false));
88+
89+
RequestInvokerHandler invoker = new RequestInvokerHandler(client, null, null, null)
90+
{
91+
InnerHandler = new TestHandler((request, token) => TestHandler.ReturnSuccess())
92+
};
93+
94+
// Act
95+
ResponseMessage response = await invoker.SendAsync(
96+
"dbs/testDb/colls/testColl",
97+
ResourceType.Document,
98+
operationType,
99+
null,
100+
containerMock.Object,
101+
feedRange,
102+
null,
103+
null,
104+
NoOpTrace.Singleton,
105+
CancellationToken.None);
106+
107+
// Assert
108+
Assert.IsNotNull(response, "Response should not be null.");
109+
110+
if (shouldThrowGoneException)
111+
{
112+
Assert.IsFalse(response.IsSuccessStatusCode, "Expected a failure status code for Query operation.");
113+
Assert.AreEqual(HttpStatusCode.Gone, response.StatusCode, "Expected a 410 Gone status code.");
114+
Assert.AreEqual((int)SubStatusCodes.PartitionKeyRangeGone, (int)response.Headers.SubStatusCode, "Expected PartitionKeyRangeGone sub-status code.");
115+
}
116+
else
117+
{
118+
Assert.IsTrue(response.IsSuccessStatusCode, $"Expected a successful status code, but got {response.StatusCode}.");
119+
}
120+
}
121+
122+
// Custom MockDocumentClient that allows injecting our TestPartitionKeyRangeCache
123+
private class CustomMockDocumentClient : MockDocumentClient
124+
{
125+
private readonly TestPartitionKeyRangeCache testPartitionKeyRangeCache;
126+
127+
public CustomMockDocumentClient(TestPartitionKeyRangeCache testPartitionKeyRangeCache)
128+
: base(new ConnectionPolicy())
129+
{
130+
this.testPartitionKeyRangeCache = testPartitionKeyRangeCache;
131+
}
132+
133+
internal override Task<PartitionKeyRangeCache> GetPartitionKeyRangeCacheAsync(ITrace trace)
134+
{
135+
return Task.FromResult<PartitionKeyRangeCache>(this.testPartitionKeyRangeCache);
136+
}
137+
}
138+
139+
private class TestPartitionKeyRangeCache : PartitionKeyRangeCache
140+
{
141+
private readonly IReadOnlyList<PartitionKeyRange> overlappingRanges;
142+
143+
public TestPartitionKeyRangeCache(IReadOnlyList<PartitionKeyRange> overlappingRanges)
144+
: base(null, null, null, null) // Pass nulls or mocks as needed for base constructor
145+
{
146+
this.overlappingRanges = overlappingRanges;
147+
}
148+
149+
public override Task<IReadOnlyList<PartitionKeyRange>> TryGetOverlappingRangesAsync(
150+
string collectionRid,
151+
Documents.Routing.Range<string> range,
152+
ITrace trace,
153+
bool forceRefresh)
154+
{
155+
return Task.FromResult(this.overlappingRanges);
156+
}
157+
}
158+
21159

22160
[TestMethod]
23161
public async Task RetryHandlerDoesNotRetryOnSuccess()

changelog.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ Preview features are treated as a separate branch and will not be included in th
1515
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
1616
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
1717

18+
### <a name="3.53.0-preview.1"/> [3.53.0-preview.1](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.53.0-preview.1) - 2025-7-10
19+
20+
### <a name="3.52.1"/> [3.52.1](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.52.1) - 2025-7-10
21+
22+
#### Fixed
23+
- [5257](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5257) QueryPlan: Fixes 410 Gone exception on query plan calls in Non-X64 windows platforms
24+
1825
### <a name="3.53.0-preview.0"/> [3.53.0-preview.0](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.53.0-preview.0) - 2025-6-13
1926

2027
### <a name="3.52.0"/> [3.52.0](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.52.0) - 2025-6-13

0 commit comments

Comments
 (0)