From d2bd043dacd264c641bb0a58f2f2c35cf3672c21 Mon Sep 17 00:00:00 2001 From: j82w Date: Thu, 1 Aug 2019 07:17:01 -0700 Subject: [PATCH] Fixed a bug with read feed with partition key. (#612) * Fixed a bug with read feed with partition key. Added additional tests for this scenario. * Additional linq tests * Fixing a coding convention * ChangeLog included * Fixing test --- .../src/Handler/RequestMessage.cs | 8 +- .../src/Handler/RouterHandler.cs | 2 +- .../CosmosBasicQueryTests.cs | 105 ++++++++++++++---- .../ResponseMessageTests.cs | 10 +- .../ResultSetIteratorTests.cs | 2 +- changelog.md | 3 + 6 files changed, 100 insertions(+), 30 deletions(-) diff --git a/Microsoft.Azure.Cosmos/src/Handler/RequestMessage.cs b/Microsoft.Azure.Cosmos/src/Handler/RequestMessage.cs index 638492f779..39edf393aa 100644 --- a/Microsoft.Azure.Cosmos/src/Handler/RequestMessage.cs +++ b/Microsoft.Azure.Cosmos/src/Handler/RequestMessage.cs @@ -91,9 +91,13 @@ public virtual Stream Content internal bool IsPropertiesInitialized => this.properties.IsValueCreated; - internal bool IsPartitionedFeedOperation => this.OperationType == OperationType.ReadFeed && + /// + /// The partition key range handler is only needed for read feed on partitioned resources + /// where the partition key range needs to be computed. + /// + internal bool IsPartitionKeyRangeHandlerRequired => this.OperationType == OperationType.ReadFeed && (this.ResourceType == ResourceType.Document || this.ResourceType == ResourceType.Conflict) && - this.PartitionKeyRangeId == null; + this.PartitionKeyRangeId == null && this.Headers.PartitionKey == null; /// /// Request properties Per request context available to handlers. diff --git a/Microsoft.Azure.Cosmos/src/Handler/RouterHandler.cs b/Microsoft.Azure.Cosmos/src/Handler/RouterHandler.cs index 0ce6559eef..55c7a65207 100644 --- a/Microsoft.Azure.Cosmos/src/Handler/RouterHandler.cs +++ b/Microsoft.Azure.Cosmos/src/Handler/RouterHandler.cs @@ -40,7 +40,7 @@ public override Task SendAsync( CancellationToken cancellationToken) { RequestHandler targetHandler = null; - if (request.IsPartitionedFeedOperation) + if (request.IsPartitionKeyRangeHandlerRequired) { targetHandler = documentFeedHandler; } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosBasicQueryTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosBasicQueryTests.cs index ce74d4be45..d375aee714 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosBasicQueryTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosBasicQueryTests.cs @@ -76,7 +76,8 @@ public async Task DatabaseTest(bool directMode) List results = await this.ToListAsync( client.GetDatabaseQueryStreamIterator, client.GetDatabaseQueryIterator, - null); + null, + CosmosBasicQueryTests.RequestOptions); CollectionAssert.IsSubsetOf(createdIds, results.Select(x => x.Id).ToList()); @@ -84,7 +85,8 @@ public async Task DatabaseTest(bool directMode) List queryResults = await this.ToListAsync( client.GetDatabaseQueryStreamIterator, client.GetDatabaseQueryIterator, - "select * from T where STARTSWITH(T.id, \"BasicQueryDb\")"); + "select * from T where STARTSWITH(T.id, \"BasicQueryDb\")", + CosmosBasicQueryTests.RequestOptions); CollectionAssert.AreEquivalent(createdIds, queryResults.Select(x => x.Id).ToList()); } @@ -121,7 +123,8 @@ public async Task ContainerTest(bool directMode) List results = await this.ToListAsync( database.GetContainerQueryStreamIterator, database.GetContainerQueryIterator, - null); + null, + CosmosBasicQueryTests.RequestOptions); CollectionAssert.IsSubsetOf(createdIds, results.Select(x => x.Id).ToList()); @@ -129,7 +132,8 @@ public async Task ContainerTest(bool directMode) List queryResults = await this.ToListAsync( database.GetContainerQueryStreamIterator, database.GetContainerQueryIterator, - "select * from T where STARTSWITH(T.id, \"BasicQueryContainer\")"); + "select * from T where STARTSWITH(T.id, \"BasicQueryContainer\")", + CosmosBasicQueryTests.RequestOptions); CollectionAssert.AreEquivalent(createdIds, queryResults.Select(x => x.Id).ToList()); } @@ -160,7 +164,8 @@ public async Task ItemTest(bool directMode) List queryResults = await this.ToListAsync( container.GetItemQueryStreamIterator, container.GetItemQueryIterator, - "select * from T where STARTSWITH(T.id, \"BasicQueryItem\")"); + "select * from T where STARTSWITH(T.id, \"BasicQueryItem\")", + CosmosBasicQueryTests.RequestOptions); if (queryResults.Count < 3) { @@ -178,7 +183,8 @@ public async Task ItemTest(bool directMode) queryResults = await this.ToListAsync( container.GetItemQueryStreamIterator, container.GetItemQueryIterator, - "select * from T where STARTSWITH(T.id, \"BasicQueryItem\")"); + "select * from T where STARTSWITH(T.id, \"BasicQueryItem\")", + CosmosBasicQueryTests.RequestOptions); } List ids = queryResults.Select(x => (string)x.id).ToList(); @@ -188,10 +194,54 @@ public async Task ItemTest(bool directMode) List results = await this.ToListAsync( container.GetItemQueryStreamIterator, container.GetItemQueryIterator, - null); + null, + CosmosBasicQueryTests.RequestOptions); + ids = results.Select(x => (string)x.id).ToList(); CollectionAssert.IsSubsetOf(createdIds, ids); + + //Read All with partition key + results = await this.ToListAsync( + container.GetItemQueryStreamIterator, + container.GetItemQueryIterator, + null, + new QueryRequestOptions() + { + MaxItemCount = 1, + PartitionKey = new PartitionKey("BasicQueryItem") + }); + + Assert.AreEqual(1, results.Count); + + //Read All with partition key + results = container.GetItemLinqQueryable( + allowSynchronousQueryExecution: true, + requestOptions: new QueryRequestOptions() + { + MaxItemCount = 1, + PartitionKey = new PartitionKey("BasicQueryItem") + }).ToList(); + + Assert.AreEqual(1, results.Count); + + // LINQ to feed iterator Read All with partition key + FeedIterator iterator = container.GetItemLinqQueryable( + allowSynchronousQueryExecution: true, + requestOptions: new QueryRequestOptions() + { + MaxItemCount = 1, + PartitionKey = new PartitionKey("BasicQueryItem") + }).ToFeedIterator(); + + List linqResults = new List(); + while (iterator.HasMoreResults) + { + linqResults.AddRange(await iterator.ReadNextAsync()); + } + + Assert.AreEqual(1, linqResults.Count); + Assert.AreEqual("BasicQueryItem", linqResults.First().pk.ToString()); } [TestMethod] @@ -213,7 +263,8 @@ public async Task ScriptsStoredProcedureTest(bool directMode) List queryResults = await this.ToListAsync( scripts.GetStoredProcedureQueryStreamIterator, scripts.GetStoredProcedureQueryIterator, - "select * from T where STARTSWITH(T.id, \"BasicQuerySp\")"); + "select * from T where STARTSWITH(T.id, \"BasicQuerySp\")", + CosmosBasicQueryTests.RequestOptions); if(queryResults.Count < 3) { @@ -229,7 +280,8 @@ public async Task ScriptsStoredProcedureTest(bool directMode) queryResults = await this.ToListAsync( scripts.GetStoredProcedureQueryStreamIterator, scripts.GetStoredProcedureQueryIterator, - "select * from T where STARTSWITH(T.id, \"BasicQuerySp\")"); + "select * from T where STARTSWITH(T.id, \"BasicQuerySp\")", + CosmosBasicQueryTests.RequestOptions); } CollectionAssert.AreEquivalent(createdIds, queryResults.Select(x => x.Id).ToList()); @@ -238,7 +290,8 @@ public async Task ScriptsStoredProcedureTest(bool directMode) List results = await this.ToListAsync( scripts.GetStoredProcedureQueryStreamIterator, scripts.GetStoredProcedureQueryIterator, - null); + null, + CosmosBasicQueryTests.RequestOptions); CollectionAssert.IsSubsetOf(createdIds, results.Select(x => x.Id).ToList()); } @@ -262,7 +315,8 @@ public async Task ScriptsUserDefinedFunctionTest(bool directMode) List queryResults = await this.ToListAsync( scripts.GetUserDefinedFunctionQueryStreamIterator, scripts.GetUserDefinedFunctionQueryIterator, - "select * from T where STARTSWITH(T.id, \"BasicQueryUdf\")"); + "select * from T where STARTSWITH(T.id, \"BasicQueryUdf\")", + CosmosBasicQueryTests.RequestOptions); if (queryResults.Count < 3) { @@ -278,7 +332,8 @@ public async Task ScriptsUserDefinedFunctionTest(bool directMode) queryResults = await this.ToListAsync( scripts.GetUserDefinedFunctionQueryStreamIterator, scripts.GetUserDefinedFunctionQueryIterator, - "select * from T where STARTSWITH(T.id, \"BasicQueryUdf\")"); + "select * from T where STARTSWITH(T.id, \"BasicQueryUdf\")", + CosmosBasicQueryTests.RequestOptions); } CollectionAssert.AreEquivalent(createdIds, queryResults.Select(x => x.Id).ToList()); @@ -287,7 +342,8 @@ public async Task ScriptsUserDefinedFunctionTest(bool directMode) List results = await this.ToListAsync( scripts.GetUserDefinedFunctionQueryStreamIterator, scripts.GetUserDefinedFunctionQueryIterator, - null); + null, + CosmosBasicQueryTests.RequestOptions); CollectionAssert.IsSubsetOf(createdIds, results.Select(x => x.Id).ToList()); } @@ -311,7 +367,8 @@ public async Task ScriptsTriggerTest(bool directMode) List queryResults = await this.ToListAsync( scripts.GetTriggerQueryStreamIterator, scripts.GetTriggerQueryIterator, - "select * from T where STARTSWITH(T.id, \"BasicQueryTrigger\")"); + "select * from T where STARTSWITH(T.id, \"BasicQueryTrigger\")", + CosmosBasicQueryTests.RequestOptions); if (queryResults.Count < 3) { @@ -327,7 +384,8 @@ public async Task ScriptsTriggerTest(bool directMode) queryResults = await this.ToListAsync( scripts.GetTriggerQueryStreamIterator, scripts.GetTriggerQueryIterator, - "select * from T where STARTSWITH(T.id, \"BasicQueryTrigger\")"); + "select * from T where STARTSWITH(T.id, \"BasicQueryTrigger\")", + CosmosBasicQueryTests.RequestOptions); } CollectionAssert.AreEquivalent(createdIds, queryResults.Select(x => x.Id).ToList()); @@ -336,7 +394,8 @@ public async Task ScriptsTriggerTest(bool directMode) List results = await this.ToListAsync( scripts.GetTriggerQueryStreamIterator, scripts.GetTriggerQueryIterator, - null); + null, + CosmosBasicQueryTests.RequestOptions); CollectionAssert.IsSubsetOf(createdIds, results.Select(x => x.Id).ToList()); } @@ -344,9 +403,13 @@ public async Task ScriptsTriggerTest(bool directMode) private delegate FeedIterator Query(string querytext, string continuationToken, QueryRequestOptions options); private delegate FeedIterator QueryStream(string querytext, string continuationToken, QueryRequestOptions options); - private async Task> ToListAsync(QueryStream createStreamQuery, Query createQuery, string queryText) + private async Task> ToListAsync( + QueryStream createStreamQuery, + Query createQuery, + string queryText, + QueryRequestOptions requestOptions) { - FeedIterator feedStreamIterator = createStreamQuery(queryText, null, RequestOptions); + FeedIterator feedStreamIterator = createStreamQuery(queryText, null, requestOptions); List streamResults = new List(); while (feedStreamIterator.HasMoreResults) { @@ -365,7 +428,7 @@ private async Task> ToListAsync(QueryStream createStreamQuery, Query< List pagedStreamResults = new List(); do { - FeedIterator pagedFeedIterator = createStreamQuery(queryText, continuationToken, RequestOptions); + FeedIterator pagedFeedIterator = createStreamQuery(queryText, continuationToken, requestOptions); ResponseMessage response = await pagedFeedIterator.ReadNextAsync(); response.EnsureSuccessStatusCode(); @@ -384,7 +447,7 @@ private async Task> ToListAsync(QueryStream createStreamQuery, Query< string streamPagedResultString = JsonConvert.SerializeObject(pagedStreamResults); Assert.AreEqual(streamPagedResultString, streamResultString); - FeedIterator feedIterator = createQuery(queryText, null, RequestOptions); + FeedIterator feedIterator = createQuery(queryText, null, requestOptions); List results = new List(); while (feedIterator.HasMoreResults) { @@ -399,7 +462,7 @@ private async Task> ToListAsync(QueryStream createStreamQuery, Query< List pagedResults = new List(); do { - FeedIterator pagedFeedIterator = createQuery(queryText, continuationToken, RequestOptions); + FeedIterator pagedFeedIterator = createQuery(queryText, continuationToken, requestOptions); FeedResponse iterator = await pagedFeedIterator.ReadNextAsync(); Assert.IsTrue(iterator.Count <= 1); Assert.IsTrue(iterator.Resource.Count() <= 1); diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ResponseMessageTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ResponseMessageTests.cs index 92b8f361e5..118ec12a14 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ResponseMessageTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ResponseMessageTests.cs @@ -16,7 +16,7 @@ public void IsFeedOperation_ForDocumentReads() RequestMessage request = new RequestMessage(); request.OperationType = OperationType.ReadFeed; request.ResourceType = ResourceType.Document; - Assert.IsTrue(request.IsPartitionedFeedOperation); + Assert.IsTrue(request.IsPartitionKeyRangeHandlerRequired); } [TestMethod] @@ -25,7 +25,7 @@ public void IsFeedOperation_ForConflictReads() RequestMessage request = new RequestMessage(); request.OperationType = OperationType.ReadFeed; request.ResourceType = ResourceType.Conflict; - Assert.IsTrue(request.IsPartitionedFeedOperation); + Assert.IsTrue(request.IsPartitionKeyRangeHandlerRequired); } [TestMethod] @@ -35,7 +35,7 @@ public void IsFeedOperation_ForChangeFeed() request.OperationType = OperationType.ReadFeed; request.ResourceType = ResourceType.Document; request.PartitionKeyRangeId = new PartitionKeyRangeIdentity("something"); - Assert.IsFalse(request.IsPartitionedFeedOperation); + Assert.IsFalse(request.IsPartitionKeyRangeHandlerRequired); } [TestMethod] @@ -44,12 +44,12 @@ public void IsFeedOperation_ForOtherOperations() RequestMessage request = new RequestMessage(); request.OperationType = OperationType.Upsert; request.ResourceType = ResourceType.Document; - Assert.IsFalse(request.IsPartitionedFeedOperation); + Assert.IsFalse(request.IsPartitionKeyRangeHandlerRequired); RequestMessage request2 = new RequestMessage(); request2.OperationType = OperationType.ReadFeed; request2.ResourceType = ResourceType.Database; - Assert.IsFalse(request2.IsPartitionedFeedOperation); + Assert.IsFalse(request2.IsPartitionKeyRangeHandlerRequired); } } } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ResultSetIteratorTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ResultSetIteratorTests.cs index 3ca7228f2c..04cc8c3f72 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ResultSetIteratorTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ResultSetIteratorTests.cs @@ -63,7 +63,7 @@ public async Task CosmosConflictsIteratorBuildsSettings() TestHandler testHandler = new TestHandler((request, cancellationToken) => { - Assert.IsTrue(request.IsPartitionedFeedOperation); + Assert.IsTrue(request.IsPartitionKeyRangeHandlerRequired); Assert.AreEqual(OperationType.ReadFeed, request.OperationType); Assert.AreEqual(ResourceType.Conflict, request.ResourceType); ResponseMessage handlerResponse = TestHandler.ReturnSuccess().Result; diff --git a/changelog.md b/changelog.md index 69991d3563..b0ae1e71e9 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Fixed +- [#612] (https://github.com/Azure/azure-cosmos-dotnet-v3/pull/612) Bug fix for ReadFeed with partition-key + ## [3.1.0](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.1.0) - 2019-07-26 ### Added