Skip to content

Commit ee22bfd

Browse files
Implement fix for LINQ Take() modifying original QueryRequestOptions
Co-authored-by: kundadebdatta <87335885+kundadebdatta@users.noreply.github.com>
1 parent 728b3f3 commit ee22bfd

2 files changed

Lines changed: 119 additions & 2 deletions

File tree

Microsoft.Azure.Cosmos/src/Query/v3Query/CosmosQueryClientCore.cs

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,13 +142,56 @@ public override async Task<TryCatch<QueryPage>> ExecuteItemQueryAsync(
142142
ITrace trace,
143143
CancellationToken cancellationToken)
144144
{
145-
requestOptions.MaxItemCount = pageSize;
145+
// Create a copy of the requestOptions to avoid modifying the original object
146+
// that users might be caching. This fixes issue #5225.
147+
QueryRequestOptions requestOptionsCopy = new QueryRequestOptions
148+
{
149+
ResponseContinuationTokenLimitInKb = requestOptions.ResponseContinuationTokenLimitInKb,
150+
EnableScanInQuery = requestOptions.EnableScanInQuery,
151+
EnableLowPrecisionOrderBy = requestOptions.EnableLowPrecisionOrderBy,
152+
EnableOptimisticDirectExecution = requestOptions.EnableOptimisticDirectExecution,
153+
MaxBufferedItemCount = requestOptions.MaxBufferedItemCount,
154+
MaxItemCount = requestOptions.MaxItemCount,
155+
MaxConcurrency = requestOptions.MaxConcurrency,
156+
PartitionKey = requestOptions.PartitionKey,
157+
PopulateIndexMetrics = requestOptions.PopulateIndexMetrics,
158+
PopulateQueryAdvice = requestOptions.PopulateQueryAdvice,
159+
ConsistencyLevel = requestOptions.ConsistencyLevel,
160+
SessionToken = requestOptions.SessionToken,
161+
DedicatedGatewayRequestOptions = requestOptions.DedicatedGatewayRequestOptions,
162+
QueryTextMode = requestOptions.QueryTextMode,
163+
CosmosElementContinuationToken = requestOptions.CosmosElementContinuationToken,
164+
StartId = requestOptions.StartId,
165+
EndId = requestOptions.EndId,
166+
EnumerationDirection = requestOptions.EnumerationDirection,
167+
CosmosSerializationFormatOptions = requestOptions.CosmosSerializationFormatOptions,
168+
SupportedSerializationFormats = requestOptions.SupportedSerializationFormats,
169+
ReturnResultsInDeterministicOrder = requestOptions.ReturnResultsInDeterministicOrder,
170+
TestSettings = requestOptions.TestSettings,
171+
FeedRange = requestOptions.FeedRange,
172+
IsHybridSearchQueryPlanOptimizationDisabled = requestOptions.IsHybridSearchQueryPlanOptimizationDisabled,
173+
EnableDistributedQueryGatewayMode = requestOptions.EnableDistributedQueryGatewayMode,
174+
// Base RequestOptions properties
175+
IfMatchEtag = requestOptions.IfMatchEtag,
176+
IfNoneMatchEtag = requestOptions.IfNoneMatchEtag,
177+
Properties = requestOptions.Properties,
178+
AddRequestHeaders = requestOptions.AddRequestHeaders,
179+
PriorityLevel = requestOptions.PriorityLevel,
180+
CosmosThresholdOptions = requestOptions.CosmosThresholdOptions,
181+
ExcludeRegions = requestOptions.ExcludeRegions,
182+
AvailabilityStrategy = requestOptions.AvailabilityStrategy,
183+
IsEffectivePartitionKeyRouting = requestOptions.IsEffectivePartitionKeyRouting,
184+
BaseConsistencyLevel = requestOptions.BaseConsistencyLevel
185+
};
186+
187+
// Now modify the copy instead of the original
188+
requestOptionsCopy.MaxItemCount = pageSize;
146189

147190
ResponseMessage message = await this.clientContext.ProcessResourceOperationStreamAsync(
148191
resourceUri: resourceUri,
149192
resourceType: resourceType,
150193
operationType: operationType,
151-
requestOptions: requestOptions,
194+
requestOptions: requestOptionsCopy,
152195
feedRange: feedRange,
153196
cosmosContainerCore: this.cosmosContainerCore,
154197
streamPayload: this.clientContext.SerializerCore.ToStreamSqlQuerySpec(sqlQuerySpec, resourceType),

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

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ namespace Microsoft.Azure.Cosmos
66
{
77
using System;
88
using System.Collections.Generic;
9+
using System.IO;
910
using System.Text;
1011
using Microsoft.VisualStudio.TestTools.UnitTesting;
1112

@@ -22,5 +23,78 @@ public void StatelessTest()
2223

2324
Assert.IsNull(testMessage.Headers.ContinuationToken);
2425
}
26+
27+
[TestMethod]
28+
public void MaxItemCountNotModifiedInOriginalQueryRequestOptions_SimpleCopyTest()
29+
{
30+
// This test verifies that when QueryRequestOptions properties are copied,
31+
// the original object is not modified. This reproduces the issue described in GitHub issue #5225.
32+
33+
QueryRequestOptions originalOptions = new QueryRequestOptions
34+
{
35+
MaxItemCount = -1,
36+
MaxConcurrency = 10,
37+
EnableScanInQuery = true,
38+
SessionToken = "test-session-token"
39+
};
40+
41+
int originalMaxItemCount = originalOptions.MaxItemCount.Value;
42+
int? originalMaxConcurrency = originalOptions.MaxConcurrency;
43+
bool? originalEnableScanInQuery = originalOptions.EnableScanInQuery;
44+
string originalSessionToken = originalOptions.SessionToken;
45+
46+
// Simulate the copy logic that would happen in CosmosQueryClientCore.ExecuteItemQueryAsync
47+
QueryRequestOptions requestOptionsCopy = new QueryRequestOptions
48+
{
49+
ResponseContinuationTokenLimitInKb = originalOptions.ResponseContinuationTokenLimitInKb,
50+
EnableScanInQuery = originalOptions.EnableScanInQuery,
51+
EnableLowPrecisionOrderBy = originalOptions.EnableLowPrecisionOrderBy,
52+
EnableOptimisticDirectExecution = originalOptions.EnableOptimisticDirectExecution,
53+
MaxBufferedItemCount = originalOptions.MaxBufferedItemCount,
54+
MaxItemCount = originalOptions.MaxItemCount,
55+
MaxConcurrency = originalOptions.MaxConcurrency,
56+
PartitionKey = originalOptions.PartitionKey,
57+
PopulateIndexMetrics = originalOptions.PopulateIndexMetrics,
58+
PopulateQueryAdvice = originalOptions.PopulateQueryAdvice,
59+
ConsistencyLevel = originalOptions.ConsistencyLevel,
60+
SessionToken = originalOptions.SessionToken,
61+
DedicatedGatewayRequestOptions = originalOptions.DedicatedGatewayRequestOptions,
62+
QueryTextMode = originalOptions.QueryTextMode,
63+
// Base RequestOptions properties
64+
IfMatchEtag = originalOptions.IfMatchEtag,
65+
IfNoneMatchEtag = originalOptions.IfNoneMatchEtag,
66+
Properties = originalOptions.Properties,
67+
AddRequestHeaders = originalOptions.AddRequestHeaders,
68+
PriorityLevel = originalOptions.PriorityLevel,
69+
CosmosThresholdOptions = originalOptions.CosmosThresholdOptions,
70+
ExcludeRegions = originalOptions.ExcludeRegions
71+
};
72+
73+
// Simulate the modification that would happen in ExecuteItemQueryAsync
74+
int pageSize = 5;
75+
requestOptionsCopy.MaxItemCount = pageSize;
76+
77+
// Assert: The original QueryRequestOptions should NOT be modified
78+
Assert.AreEqual(originalMaxItemCount, originalOptions.MaxItemCount.Value,
79+
"Original QueryRequestOptions.MaxItemCount should not be modified");
80+
Assert.AreEqual(originalMaxConcurrency, originalOptions.MaxConcurrency,
81+
"Original QueryRequestOptions.MaxConcurrency should not be modified");
82+
Assert.AreEqual(originalEnableScanInQuery, originalOptions.EnableScanInQuery,
83+
"Original QueryRequestOptions.EnableScanInQuery should not be modified");
84+
Assert.AreEqual(originalSessionToken, originalOptions.SessionToken,
85+
"Original QueryRequestOptions.SessionToken should not be modified");
86+
87+
// Assert: The copy should have the new MaxItemCount
88+
Assert.AreEqual(pageSize, requestOptionsCopy.MaxItemCount.Value,
89+
"Copied QueryRequestOptions.MaxItemCount should be updated to pageSize");
90+
91+
// Assert: Other properties should be preserved in the copy
92+
Assert.AreEqual(originalMaxConcurrency, requestOptionsCopy.MaxConcurrency,
93+
"Copied QueryRequestOptions.MaxConcurrency should match original");
94+
Assert.AreEqual(originalEnableScanInQuery, requestOptionsCopy.EnableScanInQuery,
95+
"Copied QueryRequestOptions.EnableScanInQuery should match original");
96+
Assert.AreEqual(originalSessionToken, requestOptionsCopy.SessionToken,
97+
"Copied QueryRequestOptions.SessionToken should match original");
98+
}
2599
}
26100
}

0 commit comments

Comments
 (0)