コード例 #1
0
        public FeedRangeCompositeContinuation(
            string containerRid,
            FeedRangeInternal feedRange,
            IReadOnlyList <Documents.Routing.Range <string> > ranges,
            string continuation = null)
            : this(containerRid, feedRange)
        {
            if (ranges == null)
            {
                throw new ArgumentNullException(nameof(ranges));
            }

            if (ranges.Count == 0)
            {
                throw new ArgumentOutOfRangeException(nameof(ranges));
            }

            foreach (Documents.Routing.Range <string> range in ranges)
            {
                this.CompositeContinuationTokens.Enqueue(
                    FeedRangeCompositeContinuation.CreateCompositeContinuationTokenForRange(
                        range.Min,
                        range.Max,
                        continuation));
            }

            this.CurrentToken = this.CompositeContinuationTokens.Peek();
        }
コード例 #2
0
        public void MoveToNextToken()
        {
            CompositeContinuationToken recentToken = this.compositeContinuationTokens.Dequeue();

            this.compositeContinuationTokens.Enqueue(recentToken);
            this.currentToken = this.compositeContinuationTokens.Peek();
        }
コード例 #3
0
        public void TestMatchRangesTocontinuationTokens_OneToNone()
        {
            PartitionKeyRange partitionKeyRange = new PartitionKeyRange()
            {
                MinInclusive = string.Empty,
                MaxExclusive = "A",
                Id           = "1"
            };

            CompositeContinuationToken token = new CompositeContinuationToken()
            {
                Range = new Documents.Routing.Range <string>(
                    min: "B",
                    max: "C",
                    isMinInclusive: true,
                    isMaxInclusive: false),
                Token = "asdf"
            };

            IReadOnlyDictionary <PartitionKeyRange, IPartitionedToken> expectedMapping = new Dictionary <PartitionKeyRange, IPartitionedToken>()
            {
                { partitionKeyRange, null },
            };

            ContinuationResumeLogicTests.RunMatchRangesToContinuationTokens(
                expectedMapping,
                new PartitionKeyRange[] { partitionKeyRange },
                new CompositeContinuationToken[] { token });
        }
コード例 #4
0
        public FeedTokenEPKRange(
            string containerRid,
            Documents.Routing.Range <string> completeRange,
            IReadOnlyList <CompositeContinuationToken> deserializedTokens)
            : this(containerRid)
        {
            if (deserializedTokens == null)
            {
                throw new ArgumentNullException(nameof(deserializedTokens));
            }

            if (completeRange == null)
            {
                throw new ArgumentNullException(nameof(completeRange));
            }

            if (deserializedTokens.Count == 0)
            {
                throw new ArgumentOutOfRangeException(nameof(deserializedTokens));
            }

            this.CompleteRange = completeRange;

            foreach (CompositeContinuationToken token in deserializedTokens)
            {
                this.CompositeContinuationTokens.Enqueue(token);
            }

            this.currentToken = this.CompositeContinuationTokens.Peek();
        }
コード例 #5
0
        private static bool TryParseAsCompositeContinuationToken(
            string providedContinuation,
            out CompositeContinuationToken compositeContinuationToken)
        {
            compositeContinuationToken = null;
            try
            {
                if (providedContinuation.Trim().StartsWith("[", StringComparison.Ordinal))
                {
                    List <CompositeContinuationToken> compositeContinuationTokens = JsonConvert.DeserializeObject <List <CompositeContinuationToken> >(providedContinuation);

                    if (compositeContinuationTokens != null && compositeContinuationTokens.Count > 0)
                    {
                        compositeContinuationToken = compositeContinuationTokens[0];
                    }

                    return(compositeContinuationToken != null);
                }
                else if (providedContinuation.Trim().StartsWith("{", StringComparison.Ordinal))
                {
                    compositeContinuationToken = JsonConvert.DeserializeObject <CompositeContinuationToken>(providedContinuation);
                    return(compositeContinuationToken != null);
                }

                return(false);
            }
            catch (JsonException)
            {
                return(false);
            }
        }
コード例 #6
0
        public void TestTryGetInitializationInfo_ResumeRightPartition()
        {
            PartitionKeyRange pkRange1 = new PartitionKeyRange()
            {
                MinInclusive = string.Empty,
                MaxExclusive = "A",
                Id           = "1"
            };

            PartitionKeyRange pkRange2 = new PartitionKeyRange()
            {
                MinInclusive = "A",
                MaxExclusive = "B",
                Id           = "2"
            };

            PartitionKeyRange pkRange3 = new PartitionKeyRange()
            {
                MinInclusive = "B",
                MaxExclusive = "C",
                Id           = "3"
            };

            CompositeContinuationToken token = new CompositeContinuationToken()
            {
                Range = new Documents.Routing.Range <string>(
                    min: "B",
                    max: "C",
                    isMinInclusive: true,
                    isMaxInclusive: false),
                Token = "asdf"
            };

            IReadOnlyDictionary <PartitionKeyRange, IPartitionedToken> expectedMappingLeftPartitions = new Dictionary <PartitionKeyRange, IPartitionedToken>()
            {
                { pkRange1, null },
                { pkRange2, null },
            };

            IReadOnlyDictionary <PartitionKeyRange, IPartitionedToken> expectedMappingTargetPartition = new Dictionary <PartitionKeyRange, IPartitionedToken>()
            {
                { pkRange3, token },
            };

            IReadOnlyDictionary <PartitionKeyRange, IPartitionedToken> expectedMappingRightPartitions = new Dictionary <PartitionKeyRange, IPartitionedToken>()
            {
            };

            RunTryGetInitializationInfo(
                expectedMappingLeftPartitions,
                expectedMappingTargetPartition,
                expectedMappingRightPartitions,
                new PartitionKeyRange[] { pkRange1, pkRange2, pkRange3 },
                new IPartitionedToken[] { token });
        }
コード例 #7
0
        private void InitializeCompositeTokens(IEnumerable <CompositeContinuationToken> tokens)
        {
            this.compositeContinuationTokens = new Queue <CompositeContinuationToken>();

            foreach (CompositeContinuationToken token in tokens)
            {
                this.compositeContinuationTokens.Enqueue(token);
            }

            this.currentToken = this.compositeContinuationTokens.Peek();
        }
コード例 #8
0
        private void MoveToNextToken()
        {
            CompositeContinuationToken recentToken = this.CompositeContinuationTokens.Dequeue();

            if (recentToken.Token != null)
            {
                // Normal ReadFeed can signal termination by CT null, not NotModified
                // Change Feed never lands here, as it always provides a CT
                // Consider current range done, if this FeedToken contains multiple ranges due to splits, all of them need to be considered done
                this.CompositeContinuationTokens.Enqueue(recentToken);
            }

            this.CurrentToken = this.CompositeContinuationTokens.Count > 0 ? this.CompositeContinuationTokens.Peek() : null;
        }
コード例 #9
0
        private void MoveToNextToken()
        {
            CompositeContinuationToken recentToken = this.CompositeContinuationTokens.Dequeue();

            this.CompositeContinuationTokens.Enqueue(recentToken);
            this.CurrentToken = this.CompositeContinuationTokens.Peek();

            // In a Query / ReadFeed not Change Feed, skip ranges that are done to avoid requests
            while (!this.IsDone &&
                   this.doneRanges.Contains(this.CurrentToken.Range.Min))
            {
                this.MoveToNextToken();
            }
        }
コード例 #10
0
        public FeedTokenEPKRange(
            string containerRid,
            Documents.PartitionKeyRange keyRange)
            : this(containerRid)
        {
            if (keyRange == null)
            {
                throw new ArgumentNullException(nameof(keyRange));
            }

            this.CompleteRange = new Documents.Routing.Range <string>(keyRange.MinInclusive, keyRange.MaxExclusive, true, false);
            this.CompositeContinuationTokens.Enqueue(FeedTokenEPKRange.CreateCompositeContinuationTokenForRange(keyRange.MinInclusive, keyRange.MaxExclusive, null));

            this.currentToken = this.CompositeContinuationTokens.Peek();
        }
コード例 #11
0
        private FeedTokenEPKRange(
            string containerRid,
            CompositeContinuationToken compositeContinuationTokenByPartitionKeyRangeId)
            : this(containerRid)
        {
            if (compositeContinuationTokenByPartitionKeyRangeId == null)
            {
                throw new ArgumentNullException(nameof(compositeContinuationTokenByPartitionKeyRangeId));
            }

            this.CompleteRange = compositeContinuationTokenByPartitionKeyRangeId.Range;
            this.CompositeContinuationTokens.Enqueue(compositeContinuationTokenByPartitionKeyRangeId);

            this.currentToken = this.CompositeContinuationTokens.Peek();
        }
コード例 #12
0
        public void CompositeContinuationTokenIsNotPassedToBackend()
        {
            Range <string>             expectedRange = new Range <string>("A", "B", true, false);
            string                     expectedToken = "someToken";
            CompositeContinuationToken compositeContinuationToken = new CompositeContinuationToken {
                Range = expectedRange, Token = expectedToken
            };
            string continuation = JsonConvert.SerializeObject(compositeContinuationToken);
            PartitionRoutingHelper        partitionRoutingHelper = new PartitionRoutingHelper();
            DictionaryNameValueCollection headers = new DictionaryNameValueCollection();

            headers.Add(HttpConstants.HttpHeaders.Continuation, continuation);
            Range <string> range = partitionRoutingHelper.ExtractPartitionKeyRangeFromContinuationToken(headers, out List <CompositeContinuationToken> compositeContinuationTokens);

            Assert.IsTrue(expectedRange.Equals(range));
            Assert.AreEqual(expectedToken, headers.Get(HttpConstants.HttpHeaders.Continuation)); //not a composite token
        }
コード例 #13
0
        public FeedTokenEPKRange(
            string containerRid,
            IReadOnlyList <Documents.PartitionKeyRange> keyRanges)
            : this(containerRid)
        {
            if (keyRanges == null)
            {
                throw new ArgumentNullException(nameof(keyRanges));
            }

            if (keyRanges.Count == 0)
            {
                throw new ArgumentOutOfRangeException(nameof(keyRanges));
            }

            this.CompleteRange = new Documents.Routing.Range <string>(keyRanges[0].MinInclusive, keyRanges[keyRanges.Count - 1].MaxExclusive, true, false);
            foreach (Documents.PartitionKeyRange keyRange in keyRanges)
            {
                this.CompositeContinuationTokens.Enqueue(FeedTokenEPKRange.CreateCompositeContinuationTokenForRange(keyRange.MinInclusive, keyRange.MaxExclusive, null));
            }

            this.currentToken = this.CompositeContinuationTokens.Peek();
        }
コード例 #14
0
        /// <summary>
        /// Used for deserialization only
        /// </summary>
        public FeedRangeCompositeContinuation(
            string containerRid,
            FeedRangeInternal feedRange,
            IReadOnlyList <CompositeContinuationToken> deserializedTokens)
            : this(containerRid, feedRange)
        {
            if (deserializedTokens == null)
            {
                throw new ArgumentNullException(nameof(deserializedTokens));
            }

            if (deserializedTokens.Count == 0)
            {
                throw new ArgumentOutOfRangeException(nameof(deserializedTokens));
            }

            foreach (CompositeContinuationToken token in deserializedTokens)
            {
                this.CompositeContinuationTokens.Enqueue(token);
            }

            this.CurrentToken = this.CompositeContinuationTokens.Peek();
        }
コード例 #15
0
        public async Task GetChangeFeedTokensAsyncReturnsOnePerPartitionKeyRange()
        {
            // Setting mock to have 3 ranges, to generate 3 tokens
            MultiRangeMockDocumentClient documentClient = new MultiRangeMockDocumentClient();

            using CosmosClient client = MockCosmosUtil.CreateMockCosmosClient();
            Mock <CosmosClientContext> mockContext = new Mock <CosmosClientContext>();

            mockContext.Setup(x => x.ClientOptions).Returns(MockCosmosUtil.GetDefaultConfiguration());
            mockContext.Setup(x => x.DocumentClient).Returns(documentClient);
            mockContext.Setup(x => x.SerializerCore).Returns(MockCosmosUtil.Serializer);
            mockContext.Setup(x => x.Client).Returns(client);
            mockContext.Setup(x => x.CreateLink(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <string>())).Returns(UriFactory.CreateDocumentCollectionUri("test", "test").OriginalString);

            DatabaseInternal     db        = new DatabaseInlineCore(mockContext.Object, "test");
            ContainerInternal    container = new ContainerInlineCore(mockContext.Object, db, "test");
            IEnumerable <string> tokens    = await container.GetChangeFeedTokensAsync();

            Assert.AreEqual(3, tokens.Count());

            PartitionKeyRangeCache pkRangeCache = await documentClient.GetPartitionKeyRangeCacheAsync();

            foreach (string token in tokens)
            {
                // Validate that each token represents a StandByFeedContinuationToken with a single Range
                List <CompositeContinuationToken> deserialized = JsonConvert.DeserializeObject <List <CompositeContinuationToken> >(token);
                Assert.AreEqual(1, deserialized.Count);
                CompositeContinuationToken compositeToken = deserialized[0];

                IReadOnlyList <Documents.PartitionKeyRange> rangesForTheToken = await pkRangeCache.TryGetOverlappingRangesAsync("", compositeToken.Range);

                // Token represents one range
                Assert.AreEqual(1, rangesForTheToken.Count);
                Assert.AreEqual(rangesForTheToken[0].MinInclusive, compositeToken.Range.Min);
                Assert.AreEqual(rangesForTheToken[0].MaxExclusive, compositeToken.Range.Max);
            }
        }
コード例 #16
0
        public async Task TestCosmosOrderByQueryExecutionContextWithFailurePageAsync(bool createInitialContinuationToken)
        {
            int maxPageSize = 5;

            List <MockPartitionResponse[]> mockResponsesScenario = MockQueryFactory.GetFailureScenarios();

            Mock <CosmosQueryClient> mockQueryClient = new Mock <CosmosQueryClient>();

            foreach (MockPartitionResponse[] mockResponse in mockResponsesScenario)
            {
                string initialContinuationToken = null;
                string fullContinuationToken    = null;
                if (createInitialContinuationToken)
                {
                    ToDoItem itemToRepresentPreviousQuery = ToDoItem.CreateItems(
                        1,
                        "itemToRepresentPreviousQuery",
                        MockQueryFactory.DefaultCollectionRid).First();

                    initialContinuationToken = $" - RID:{itemToRepresentPreviousQuery._rid} ==#RT:1#TRC:1";
                    CompositeContinuationToken compositeContinuation = new CompositeContinuationToken()
                    {
                        Range = new Documents.Routing.Range <string>(
                            min: MockQueryFactory.DefaultPartitionKeyRange.MinInclusive,
                            max: MockQueryFactory.DefaultPartitionKeyRange.MaxExclusive,
                            isMaxInclusive: false,
                            isMinInclusive: true),
                        Token = initialContinuationToken
                    };

                    List <OrderByItem> orderByItems = new List <OrderByItem>()
                    {
                        new OrderByItem(CosmosObject.CreateFromBuffer(Encoding.UTF8.GetBytes("{\"item\":\"2c4ce711-13c3-4c93-817c-49287b71b6c3\"}")))
                    };

                    OrderByContinuationToken orderByContinuationToken = new OrderByContinuationToken(
                        compositeContinuationToken: compositeContinuation,
                        orderByItems: orderByItems,
                        rid: itemToRepresentPreviousQuery._rid,
                        skipCount: 0,
                        filter: null);

                    fullContinuationToken = CosmosArray.Create(
                        new List <CosmosElement>()
                    {
                        OrderByContinuationToken.ToCosmosElement(orderByContinuationToken)
                    }).ToString();
                }

                IList <ToDoItem> allItems = MockQueryFactory.GenerateAndMockResponse(
                    mockQueryClient,
                    isOrderByQuery: true,
                    sqlQuerySpec: MockQueryFactory.DefaultQuerySpec,
                    containerRid: MockQueryFactory.DefaultCollectionRid,
                    initContinuationToken: initialContinuationToken,
                    maxPageSize: maxPageSize,
                    mockResponseForSinglePartition: mockResponse,
                    cancellationTokenForMocks: this.cancellationToken);

                // Order by drains the partitions until it finds an item
                // If there are no items then it's not possible to have a continuation token
                if (allItems.Count == 0 && createInitialContinuationToken)
                {
                    continue;
                }

                CosmosQueryContext context = MockQueryFactory.CreateContext(
                    mockQueryClient.Object);

                QueryInfo queryInfo = new QueryInfo()
                {
                    OrderBy            = new SortOrder[] { SortOrder.Ascending },
                    OrderByExpressions = new string[] { "id" }
                };

                CosmosCrossPartitionQueryExecutionContext.CrossPartitionInitParams initParams = new CosmosCrossPartitionQueryExecutionContext.CrossPartitionInitParams(
                    sqlQuerySpec: MockQueryFactory.DefaultQuerySpec,
                    collectionRid: MockQueryFactory.DefaultCollectionRid,
                    partitionedQueryExecutionInfo: new PartitionedQueryExecutionInfo()
                {
                    QueryInfo = queryInfo
                },
                    partitionKeyRanges: new List <PartitionKeyRange>()
                {
                    MockQueryFactory.DefaultPartitionKeyRange
                },
                    initialPageSize: maxPageSize,
                    maxConcurrency: null,
                    maxItemCount: maxPageSize,
                    maxBufferedItemCount: null,
                    returnResultsInDeterministicOrder: true,
                    testSettings: new TestInjections(simulate429s: false, simulateEmptyPages: false));

                TryCatch <IDocumentQueryExecutionComponent> tryCreate = await CosmosOrderByItemQueryExecutionContext.TryCreateAsync(
                    context,
                    initParams,
                    fullContinuationToken != null?CosmosElement.Parse(fullContinuationToken) : null,
                    this.cancellationToken);

                if (tryCreate.Succeeded)
                {
                    IDocumentQueryExecutionComponent executionContext = tryCreate.Result;

                    Assert.IsTrue(!executionContext.IsDone);

                    // Read all the pages from both splits
                    List <ToDoItem>   itemsRead = new List <ToDoItem>();
                    QueryResponseCore?failure   = null;
                    while (!executionContext.IsDone)
                    {
                        QueryResponseCore queryResponse = await executionContext.DrainAsync(
                            maxPageSize,
                            this.cancellationToken);

                        if (queryResponse.IsSuccess)
                        {
                            string responseContinuationToken = queryResponse.ContinuationToken;
                            foreach (CosmosElement element in queryResponse.CosmosElements)
                            {
                                string   jsonValue = element.ToString();
                                ToDoItem item      = JsonConvert.DeserializeObject <ToDoItem>(jsonValue);
                                itemsRead.Add(item);
                            }
                        }
                        else
                        {
                            Assert.IsNull(failure, "There should only be one error");
                            failure = queryResponse;
                        }
                    }

                    Assert.IsNotNull(failure);
                    Assert.AreEqual((HttpStatusCode)429, failure.Value.StatusCode);
                    Assert.IsNotNull(failure.Value.CosmosException.ToString());

                    Assert.AreEqual(0 /*We don't get any items, since we don't buffer the failure anymore*/, itemsRead.Count);

                    //CollectionAssert.AreEqual(allItems.ToList(), itemsRead, new ToDoItemComparer());
                }
                else
                {
                    QueryResponseCore queryResponseCore = QueryResponseFactory.CreateFromException(tryCreate.Exception);
                    Assert.AreEqual((HttpStatusCode)429, queryResponseCore.StatusCode);
                }
            }
        }
コード例 #17
0
        private void AddFormattedContinuationHeaderHelper(AddFormattedContinuationToHeaderTestUnit positiveTestData, out INameValueCollection headers, out List <PartitionKeyRange> resolvedRanges, out List <CompositeContinuationToken> resolvedContinuationTokens)
        {
            Func <string, INameValueCollection> getHeadersWithContinuation = (string continuationToken) =>
            {
                INameValueCollection localHeaders = new StoreRequestNameValueCollection();
                if (continuationToken != null)
                {
                    localHeaders[HttpConstants.HttpHeaders.Continuation] = continuationToken;
                }
                return(localHeaders);
            };

            resolvedRanges = positiveTestData.ResolvedRanges.Select(x => new PartitionKeyRange()
            {
                MinInclusive = x.Min, MaxExclusive = x.Max
            }).ToList();
            resolvedContinuationTokens = new List <CompositeContinuationToken>();

            CompositeContinuationToken[] initialContinuationTokens = null;
            if (!string.IsNullOrEmpty(positiveTestData.InputCompositeContinuationToken))
            {
                if (positiveTestData.InputCompositeContinuationToken.Trim().StartsWith("[", StringComparison.Ordinal))
                {
                    initialContinuationTokens = JsonConvert.DeserializeObject <CompositeContinuationToken[]>(positiveTestData.InputCompositeContinuationToken);
                }
                else
                {
                    initialContinuationTokens = new CompositeContinuationToken[] { JsonConvert.DeserializeObject <CompositeContinuationToken>(positiveTestData.InputCompositeContinuationToken) };
                }
            }

            if (resolvedRanges.Count > 1)
            {
                CompositeContinuationToken continuationToBeCopied;
                if (initialContinuationTokens != null && initialContinuationTokens.Length > 0)
                {
                    continuationToBeCopied = (CompositeContinuationToken)initialContinuationTokens[0].ShallowCopy();
                }
                else
                {
                    continuationToBeCopied       = new CompositeContinuationToken();
                    continuationToBeCopied.Token = string.Empty;
                }

                headers = getHeadersWithContinuation(continuationToBeCopied.Token);

                foreach (PartitionKeyRange pkrange in resolvedRanges)
                {
                    CompositeContinuationToken token = (CompositeContinuationToken)continuationToBeCopied.ShallowCopy();
                    token.Range = pkrange.ToRange();
                    resolvedContinuationTokens.Add(token);
                }

                if (initialContinuationTokens != null)
                {
                    resolvedContinuationTokens.AddRange(initialContinuationTokens.Skip(1));
                }
            }
            else
            {
                headers = getHeadersWithContinuation(null);
            }
        }
        private static TryCatch <IReadOnlyList <CompositeContinuationToken> > TryParseCompositeContinuationList(
            CosmosElement requestContinuationToken)
        {
            if (requestContinuationToken == null)
            {
                throw new ArgumentNullException(nameof(requestContinuationToken));
            }

            if (!(requestContinuationToken is CosmosArray compositeContinuationTokenListRaw))
            {
                return(TryCatch <IReadOnlyList <CompositeContinuationToken> > .FromException(
                           new MalformedContinuationTokenException(
                               $"Invalid format for continuation token {requestContinuationToken} for {nameof(CosmosParallelItemQueryExecutionContext)}")));
            }

            List <CompositeContinuationToken> compositeContinuationTokens = new List <CompositeContinuationToken>();

            foreach (CosmosElement compositeContinuationTokenRaw in compositeContinuationTokenListRaw)
            {
                TryCatch <CompositeContinuationToken> tryCreateCompositeContinuationToken = CompositeContinuationToken.TryCreateFromCosmosElement(compositeContinuationTokenRaw);
                if (!tryCreateCompositeContinuationToken.Succeeded)
                {
                    return(TryCatch <IReadOnlyList <CompositeContinuationToken> > .FromException(tryCreateCompositeContinuationToken.Exception));
                }

                compositeContinuationTokens.Add(tryCreateCompositeContinuationToken.Result);
            }

            return(TryCatch <IReadOnlyList <CompositeContinuationToken> > .FromResult(compositeContinuationTokens));
        }
コード例 #19
0
        public override async Task <ResponseMessage> ReadNextAsync(CancellationToken cancellationToken = default)
        {
            cancellationToken.ThrowIfCancellationRequested();

            TryCatch <CrossPartitionChangeFeedAsyncEnumerator> monadicEnumerator = await this.lazyMonadicEnumerator.GetValueAsync(cancellationToken);

            if (monadicEnumerator.Failed)
            {
                Exception       createException = monadicEnumerator.Exception;
                CosmosException cosmosException = ExceptionToCosmosException.CreateFromException(createException);
                return(new ResponseMessage(
                           cosmosException.StatusCode,
                           requestMessage: null,
                           headers: cosmosException.Headers,
                           cosmosException: cosmosException,
                           diagnostics: new CosmosDiagnosticsContextCore()));
            }

            CrossPartitionChangeFeedAsyncEnumerator enumerator = monadicEnumerator.Result;

            if (!await enumerator.MoveNextAsync())
            {
                throw new InvalidOperationException("ChangeFeed enumerator should always have a next continuation");
            }

            if (enumerator.Current.Failed)
            {
                CosmosException cosmosException = ExceptionToCosmosException.CreateFromException(enumerator.Current.Exception);
                if (!IsRetriableException(cosmosException))
                {
                    this.hasMoreResults = false;
                }

                return(new ResponseMessage(
                           cosmosException.StatusCode,
                           requestMessage: null,
                           headers: cosmosException.Headers,
                           cosmosException: cosmosException,
                           diagnostics: new CosmosDiagnosticsContextCore()));
            }

            CrossFeedRangePage <Pagination.ChangeFeedPage, ChangeFeedState> crossFeedRangePage = enumerator.Current.Result;

            Pagination.ChangeFeedPage changeFeedPage = crossFeedRangePage.Page;
            ResponseMessage           responseMessage;

            if (changeFeedPage is Pagination.ChangeFeedSuccessPage changeFeedSuccessPage)
            {
                responseMessage = new ResponseMessage(statusCode: System.Net.HttpStatusCode.OK)
                {
                    Content = changeFeedSuccessPage.Content
                };
            }
            else
            {
                responseMessage = new ResponseMessage(statusCode: System.Net.HttpStatusCode.NotModified);
            }

            CrossFeedRangeState <ChangeFeedState> crossFeedRangeState = crossFeedRangePage.State;
            string continuationToken;

            if (this.changeFeedRequestOptions.EmitOldContinuationToken)
            {
                List <CompositeContinuationToken> compositeContinuationTokens = new List <CompositeContinuationToken>();
                for (int i = 0; i < crossFeedRangeState.Value.Length; i++)
                {
                    FeedRangeState <ChangeFeedState> changeFeedFeedRangeState = crossFeedRangeState.Value.Span[i];
                    string token = changeFeedFeedRangeState.State is ChangeFeedStateContinuation changeFeedStateContinuation ? ((CosmosString)changeFeedStateContinuation.ContinuationToken).Value : null;
                    Documents.Routing.Range <string> range = ((FeedRangeEpk)changeFeedFeedRangeState.FeedRange).Range;
                    CompositeContinuationToken       compositeContinuationToken = new CompositeContinuationToken()
                    {
                        Range = range,
                        Token = token,
                    };

                    compositeContinuationTokens.Add(compositeContinuationToken);
                }

                FeedRangeCompositeContinuation feedRangeCompositeContinuationToken = new FeedRangeCompositeContinuation(
                    await this.documentContainer.GetResourceIdentifierAsync(cancellationToken),
                    FeedRangeEpk.FullRange,
                    compositeContinuationTokens);

                continuationToken = feedRangeCompositeContinuationToken.ToString();
            }
            else
            {
                ChangeFeedCrossFeedRangeState changeFeedCrossFeedRangeState = new ChangeFeedCrossFeedRangeState(crossFeedRangeState.Value);
                continuationToken = VersionedAndRidCheckedCompositeToken.ToCosmosElement(
                    new VersionedAndRidCheckedCompositeToken(
                        VersionedAndRidCheckedCompositeToken.Version.V2,
                        changeFeedCrossFeedRangeState.ToCosmosElement(),
                        await this.documentContainer.GetResourceIdentifierAsync(cancellationToken))).ToString();
            }

            responseMessage.Headers.ContinuationToken = continuationToken;
            responseMessage.Headers.RequestCharge     = changeFeedPage.RequestCharge;
            responseMessage.Headers.ActivityId        = changeFeedPage.ActivityId;

            return(responseMessage);
        }
コード例 #20
0
        public override async Task <ResponseMessage> ReadNextAsync(
            ITrace trace,
            CancellationToken cancellationToken = default)
        {
            if (trace == null)
            {
                throw new ArgumentNullException(nameof(trace));
            }

            cancellationToken.ThrowIfCancellationRequested();

            if (!this.hasMoreResults)
            {
                throw new InvalidOperationException("Should not be calling FeedIterator that does not have any more results");
            }

            if (this.monadicEnumerator.Failed)
            {
                this.hasMoreResults = false;

                CosmosException cosmosException = ExceptionToCosmosException.CreateFromException(this.monadicEnumerator.Exception);
                return(new ResponseMessage(
                           statusCode: System.Net.HttpStatusCode.BadRequest,
                           requestMessage: null,
                           headers: cosmosException.Headers,
                           cosmosException: cosmosException,
                           trace: trace));
            }

            CrossPartitionReadFeedAsyncEnumerator enumerator = this.monadicEnumerator.Result;
            TryCatch <CrossFeedRangePage <Pagination.ReadFeedPage, ReadFeedState> > monadicPage;

            try
            {
                if (!await enumerator.MoveNextAsync(trace))
                {
                    throw new InvalidOperationException("Should not be calling enumerator that does not have any more results");
                }

                monadicPage = enumerator.Current;
            }
            catch (Exception ex)
            {
                monadicPage = TryCatch <CrossFeedRangePage <Pagination.ReadFeedPage, ReadFeedState> > .FromException(ex);
            }

            if (monadicPage.Failed)
            {
                CosmosException cosmosException = ExceptionToCosmosException.CreateFromException(monadicPage.Exception);
                if (!IsRetriableException(cosmosException))
                {
                    this.hasMoreResults = false;
                }

                return(new ResponseMessage(
                           statusCode: cosmosException.StatusCode,
                           requestMessage: null,
                           headers: cosmosException.Headers,
                           cosmosException: cosmosException,
                           trace: trace));
            }

            CrossFeedRangePage <Pagination.ReadFeedPage, ReadFeedState> crossFeedRangePage = monadicPage.Result;

            if (crossFeedRangePage.State == default)
            {
                this.hasMoreResults = false;
            }

            // Make the continuation token match the older format:
            string continuationToken;

            if (crossFeedRangePage.State != null)
            {
                List <CompositeContinuationToken>   compositeContinuationTokens = new List <CompositeContinuationToken>();
                CrossFeedRangeState <ReadFeedState> crossFeedRangeState         = crossFeedRangePage.State;
                for (int i = 0; i < crossFeedRangeState.Value.Length; i++)
                {
                    FeedRangeState <ReadFeedState> feedRangeState = crossFeedRangeState.Value.Span[i];
                    FeedRangeEpk feedRange;
                    if (feedRangeState.FeedRange is FeedRangeEpk feedRangeEpk)
                    {
                        feedRange = feedRangeEpk;
                    }
                    else
                    {
                        feedRange = FeedRangeEpk.FullRange;
                    }

                    ReadFeedState readFeedState = feedRangeState.State;
                    CompositeContinuationToken compositeContinuationToken = new CompositeContinuationToken()
                    {
                        Range = feedRange.Range,
                        Token = readFeedState is ReadFeedBeginningState ? null : ((ReadFeedContinuationState)readFeedState).ContinuationToken.ToString(),
                    };

                    compositeContinuationTokens.Add(compositeContinuationToken);
                }

                FeedRangeInternal outerFeedRange;
                if ((this.queryRequestOptions != null) && this.queryRequestOptions.PartitionKey.HasValue)
                {
                    outerFeedRange = new FeedRangePartitionKey(this.queryRequestOptions.PartitionKey.Value);
                }
                else if ((this.queryRequestOptions != null) && (this.queryRequestOptions.FeedRange != null))
                {
                    outerFeedRange = (FeedRangeInternal)this.queryRequestOptions.FeedRange;
                }
                else
                {
                    outerFeedRange = FeedRangeEpk.FullRange;
                }

                FeedRangeCompositeContinuation feedRangeCompositeContinuation = new FeedRangeCompositeContinuation(
                    containerRid: string.Empty,
                    feedRange: outerFeedRange,
                    compositeContinuationTokens);

                continuationToken = feedRangeCompositeContinuation.ToString();
            }
            else
            {
                continuationToken = null;
            }

            Pagination.ReadFeedPage page = crossFeedRangePage.Page;
            Headers headers = new Headers()
            {
                RequestCharge     = page.RequestCharge,
                ActivityId        = page.ActivityId,
                ContinuationToken = continuationToken,
            };

            foreach (KeyValuePair <string, string> kvp in page.AdditionalHeaders)
            {
                headers[kvp.Key] = kvp.Value;
            }

            return(new ResponseMessage(
                       statusCode: System.Net.HttpStatusCode.OK,
                       requestMessage: default,
コード例 #21
0
        public async Task TestCosmosCrossPartitionQueryExecutionContextWithFailuresAsync(bool createInitialContinuationToken)
        {
            int maxPageSize = 5;

            List <MockPartitionResponse[]> mockResponsesScenario = MockQueryFactory.GetFailureScenarios();

            foreach (MockPartitionResponse[] mockResponse in mockResponsesScenario)
            {
                string initialContinuationToken = null;
                string fullContinuationToken    = null;
                if (createInitialContinuationToken)
                {
                    initialContinuationToken = " - RID:02FYAIvUH1kCAAAAAAAAAA ==#RT:1#TRC:1";
                    CompositeContinuationToken compositeContinuation = new CompositeContinuationToken()
                    {
                        Range = new Documents.Routing.Range <string>(
                            min: MockQueryFactory.DefaultPartitionKeyRange.MinInclusive,
                            max: MockQueryFactory.DefaultPartitionKeyRange.MaxExclusive,
                            isMaxInclusive: false,
                            isMinInclusive: true),
                        Token = initialContinuationToken
                    };

                    fullContinuationToken = CosmosArray.Create(
                        new List <CosmosElement>()
                    {
                        CompositeContinuationToken.ToCosmosElement(compositeContinuation)
                    }).ToString();
                }

                Mock <CosmosQueryClient> mockQueryClient = new Mock <CosmosQueryClient>();
                IList <ToDoItem>         allItems        = MockQueryFactory.GenerateAndMockResponse(
                    mockQueryClient,
                    isOrderByQuery: false,
                    sqlQuerySpec: MockQueryFactory.DefaultQuerySpec,
                    containerRid: MockQueryFactory.DefaultCollectionRid,
                    initContinuationToken: initialContinuationToken,
                    maxPageSize: maxPageSize,
                    mockResponseForSinglePartition: mockResponse,
                    cancellationTokenForMocks: this.cancellationToken);

                CosmosQueryContext context = MockQueryFactory.CreateContext(
                    mockQueryClient.Object);

                CosmosCrossPartitionQueryExecutionContext.CrossPartitionInitParams initParams = new CosmosCrossPartitionQueryExecutionContext.CrossPartitionInitParams(
                    sqlQuerySpec: MockQueryFactory.DefaultQuerySpec,
                    collectionRid: MockQueryFactory.DefaultCollectionRid,
                    partitionedQueryExecutionInfo: new PartitionedQueryExecutionInfo()
                {
                    QueryInfo = new QueryInfo()
                },
                    partitionKeyRanges: new List <PartitionKeyRange>()
                {
                    MockQueryFactory.DefaultPartitionKeyRange
                },
                    initialPageSize: maxPageSize,
                    maxConcurrency: null,
                    maxItemCount: maxPageSize,
                    maxBufferedItemCount: null,
                    returnResultsInDeterministicOrder: true,
                    testSettings: new TestInjections(simulate429s: false, simulateEmptyPages: false));

                IDocumentQueryExecutionComponent executionContext = (await CosmosParallelItemQueryExecutionContext.TryCreateAsync(
                                                                         context,
                                                                         initParams,
                                                                         fullContinuationToken != null ? CosmosElement.Parse(fullContinuationToken) : null,
                                                                         this.cancellationToken)).Result;

                // Read all the pages from both splits
                List <ToDoItem> itemsRead = new List <ToDoItem>();
                Assert.IsTrue(!executionContext.IsDone);

                QueryResponseCore?failure = null;
                while (!executionContext.IsDone)
                {
                    QueryResponseCore queryResponse = await executionContext.DrainAsync(maxPageSize, this.cancellationToken);

                    if (queryResponse.IsSuccess)
                    {
                        string responseContinuationToken = queryResponse.ContinuationToken;
                        foreach (CosmosElement element in queryResponse.CosmosElements)
                        {
                            string   jsonValue = element.ToString();
                            ToDoItem item      = JsonConvert.DeserializeObject <ToDoItem>(jsonValue);
                            itemsRead.Add(item);
                        }
                    }
                    else
                    {
                        Assert.IsNull(failure, "There should only be one error");
                        failure = queryResponse;
                    }
                }

                Assert.IsNotNull(failure);
                Assert.AreEqual((HttpStatusCode)429, failure.Value.StatusCode);
                Assert.IsNotNull(failure.Value.CosmosException.ToString());

                Assert.AreEqual(allItems.Count, itemsRead.Count);
                List <ToDoItem> exepected = allItems.OrderBy(x => x.id).ToList();
                List <ToDoItem> actual    = itemsRead.OrderBy(x => x.id).ToList();

                CollectionAssert.AreEqual(exepected, actual, new ToDoItemComparer());
            }
        }
コード例 #22
0
        public async Task TestCosmosCrossPartitionQueryExecutionContextWithEmptyPagesAndSplitAsync(bool createInitialContinuationToken)
        {
            int maxPageSize = 5;

            List <MockPartitionResponse[]> mockResponsesScenario = MockQueryFactory.GetSplitScenarios();

            foreach (MockPartitionResponse[] mockResponse in mockResponsesScenario)
            {
                string initialContinuationToken = null;
                string fullConitnuationToken    = null;
                if (createInitialContinuationToken)
                {
                    initialContinuationToken = " - RID:02FYAIvUH1kCAAAAAAAAAA ==#RT:1#TRC:1";
                    CompositeContinuationToken compositeContinuation = new CompositeContinuationToken()
                    {
                        Range = new Documents.Routing.Range <string>(
                            min: MockQueryFactory.DefaultPartitionKeyRange.MinInclusive,
                            max: MockQueryFactory.DefaultPartitionKeyRange.MaxExclusive,
                            isMaxInclusive: false,
                            isMinInclusive: true),
                        Token = initialContinuationToken
                    };

                    fullConitnuationToken = JsonConvert.SerializeObject(new CompositeContinuationToken[] { compositeContinuation });
                }

                Mock <CosmosQueryClient> mockQueryClient = new Mock <CosmosQueryClient>();
                IList <ToDoItem>         allItems        = MockQueryFactory.GenerateAndMockResponse(
                    mockQueryClient,
                    isOrderByQuery: false,
                    sqlQuerySpec: MockQueryFactory.DefaultQuerySpec,
                    containerRid: MockQueryFactory.DefaultCollectionRid,
                    initContinuationToken: initialContinuationToken,
                    maxPageSize: maxPageSize,
                    mockResponseForSinglePartition: mockResponse,
                    cancellationTokenForMocks: this.cancellationToken);

                CosmosQueryContext context = MockQueryFactory.CreateContext(
                    mockQueryClient.Object);

                CosmosCrossPartitionQueryExecutionContext.CrossPartitionInitParams initParams = new CosmosCrossPartitionQueryExecutionContext.CrossPartitionInitParams(
                    sqlQuerySpec: MockQueryFactory.DefaultQuerySpec,
                    collectionRid: MockQueryFactory.DefaultCollectionRid,
                    partitionedQueryExecutionInfo: new PartitionedQueryExecutionInfo()
                {
                    QueryInfo = new QueryInfo()
                },
                    partitionKeyRanges: new List <PartitionKeyRange>()
                {
                    MockQueryFactory.DefaultPartitionKeyRange
                },
                    initialPageSize: maxPageSize,
                    maxConcurrency: null,
                    maxItemCount: maxPageSize,
                    maxBufferedItemCount: null);

                CosmosParallelItemQueryExecutionContext executionContext = await CosmosParallelItemQueryExecutionContext.CreateAsync(
                    context,
                    initParams,
                    fullConitnuationToken,
                    this.cancellationToken);

                // Read all the pages from both splits
                List <ToDoItem> itemsRead = new List <ToDoItem>();
                Assert.IsTrue(!executionContext.IsDone);

                while (!executionContext.IsDone)
                {
                    QueryResponseCore queryResponse = await executionContext.DrainAsync(maxPageSize, this.cancellationToken);

                    string responseContinuationToken = queryResponse.ContinuationToken;
                    foreach (CosmosElement element in queryResponse.CosmosElements)
                    {
                        string   jsonValue = element.ToString();
                        ToDoItem item      = JsonConvert.DeserializeObject <ToDoItem>(jsonValue);
                        itemsRead.Add(item);
                    }
                }

                Assert.AreEqual(allItems.Count, itemsRead.Count);
                List <ToDoItem> exepected = allItems.OrderBy(x => x.id).ToList();
                List <ToDoItem> actual    = itemsRead.OrderBy(x => x.id).ToList();

                CollectionAssert.AreEqual(exepected, actual, new ToDoItemComparer());
            }
        }
コード例 #23
0
        public async Task TestCosmosOrderByQueryExecutionContextWithEmptyPagesAndSplitAsync(bool createInitialContinuationToken)
        {
            int maxPageSize = 5;

            List <MockPartitionResponse[]> mockResponsesScenario = MockQueryFactory.GetSplitScenarios();

            foreach (MockPartitionResponse[] mockResponse in mockResponsesScenario)
            {
                string initialContinuationToken = null;
                string fullConitnuationToken    = null;
                if (createInitialContinuationToken)
                {
                    ToDoItem itemToRepresentPreviousQuery = ToDoItem.CreateItems(
                        1,
                        "itemToRepresentPreviousQuery",
                        MockQueryFactory.DefaultCollectionRid).First();

                    initialContinuationToken = $" - RID:{itemToRepresentPreviousQuery._rid} ==#RT:1#TRC:1";
                    CompositeContinuationToken compositeContinuation = new CompositeContinuationToken()
                    {
                        Range = new Documents.Routing.Range <string>(
                            min: MockQueryFactory.DefaultPartitionKeyRange.MinInclusive,
                            max: MockQueryFactory.DefaultPartitionKeyRange.MaxExclusive,
                            isMaxInclusive: false,
                            isMinInclusive: true),
                        Token = initialContinuationToken
                    };

                    List <OrderByItem> orderByItems = new List <OrderByItem>()
                    {
                        new OrderByItem(CosmosObject.Create(Encoding.UTF8.GetBytes("{\"item\":\"2c4ce711-13c3-4c93-817c-49287b71b6c3\"}")))
                    };

                    OrderByContinuationToken orderByContinuationToken = new OrderByContinuationToken(
                        compositeContinuationToken: compositeContinuation,
                        orderByItems: orderByItems,
                        rid: itemToRepresentPreviousQuery._rid,
                        skipCount: 0,
                        filter: null);

                    fullConitnuationToken = JsonConvert.SerializeObject(new OrderByContinuationToken[] { orderByContinuationToken });
                }

                Mock <CosmosQueryClient> mockQueryClient = new Mock <CosmosQueryClient>();
                IList <ToDoItem>         allItems        = MockQueryFactory.GenerateAndMockResponse(
                    mockQueryClient,
                    isOrderByQuery: true,
                    sqlQuerySpec: MockQueryFactory.DefaultQuerySpec,
                    containerRid: MockQueryFactory.DefaultCollectionRid,
                    initContinuationToken: initialContinuationToken,
                    maxPageSize: maxPageSize,
                    mockResponseForSinglePartition: mockResponse,
                    cancellationTokenForMocks: this.cancellationToken);

                // Order by drains the partitions until it finds an item
                // If there are no items then it's not possible to have a continuation token
                if (allItems.Count == 0 && createInitialContinuationToken)
                {
                    continue;
                }

                CosmosQueryContext context = MockQueryFactory.CreateContext(
                    mockQueryClient.Object);

                QueryInfo queryInfo = new QueryInfo()
                {
                    OrderBy            = new SortOrder[] { SortOrder.Ascending },
                    OrderByExpressions = new string[] { "id" }
                };

                CosmosCrossPartitionQueryExecutionContext.CrossPartitionInitParams initParams = new CosmosCrossPartitionQueryExecutionContext.CrossPartitionInitParams(
                    sqlQuerySpec: MockQueryFactory.DefaultQuerySpec,
                    collectionRid: MockQueryFactory.DefaultCollectionRid,
                    partitionedQueryExecutionInfo: new PartitionedQueryExecutionInfo()
                {
                    QueryInfo = queryInfo
                },
                    partitionKeyRanges: new List <PartitionKeyRange>()
                {
                    MockQueryFactory.DefaultPartitionKeyRange
                },
                    initialPageSize: maxPageSize,
                    maxConcurrency: null,
                    maxItemCount: maxPageSize,
                    maxBufferedItemCount: null);

                CosmosOrderByItemQueryExecutionContext executionContext = await CosmosOrderByItemQueryExecutionContext.CreateAsync(
                    context,
                    initParams,
                    fullConitnuationToken,
                    this.cancellationToken);

                // For order by it will drain all the pages till it gets a value.
                if (allItems.Count == 0)
                {
                    Assert.IsTrue(executionContext.IsDone);
                    continue;
                }

                Assert.IsTrue(!executionContext.IsDone);

                // Read all the pages from both splits
                List <ToDoItem> itemsRead = new List <ToDoItem>();
                while (!executionContext.IsDone)
                {
                    QueryResponseCore queryResponse = await executionContext.DrainAsync(
                        maxPageSize,
                        this.cancellationToken);

                    string responseContinuationToken = queryResponse.ContinuationToken;
                    foreach (CosmosElement element in queryResponse.CosmosElements)
                    {
                        string   jsonValue = element.ToString();
                        ToDoItem item      = JsonConvert.DeserializeObject <ToDoItem>(jsonValue);
                        itemsRead.Add(item);
                    }
                }

                Assert.AreEqual(allItems.Count, itemsRead.Count);

                CollectionAssert.AreEqual(allItems.ToList(), itemsRead, new ToDoItemComparer());
            }
        }
コード例 #24
0
        /// <summary>
        /// Get the next set of results from the cosmos service
        /// </summary>
        /// <param name="cancellationToken">(Optional) <see cref="CancellationToken"/> representing request cancellation.</param>
        /// <returns>A query response from cosmos service</returns>
        public override async Task <ResponseMessage> ReadNextAsync(CancellationToken cancellationToken = default)
        {
            cancellationToken.ThrowIfCancellationRequested();

            if (!this.hasMoreResults)
            {
                throw new InvalidOperationException("Should not be calling FeedIterator that does not have any more results");
            }

            if (this.monadicEnumerator.Failed)
            {
                this.hasMoreResults = false;

                CosmosException cosmosException = ExceptionToCosmosException.CreateFromException(this.monadicEnumerator.Exception);
                return(new ResponseMessage(
                           statusCode: System.Net.HttpStatusCode.BadRequest,
                           requestMessage: null,
                           headers: cosmosException.Headers,
                           cosmosException: cosmosException,
                           diagnostics: cosmosException.DiagnosticsContext));
            }

            CrossPartitionReadFeedAsyncEnumerator enumerator = this.monadicEnumerator.Result;
            TryCatch <ReadFeedPage> monadicPage;

            try
            {
                if (!await enumerator.MoveNextAsync())
                {
                    throw new InvalidOperationException("Should not be calling enumerator that does not have any more results");
                }

                monadicPage = enumerator.Current;
            }
            catch (Exception ex)
            {
                monadicPage = TryCatch <ReadFeedPage> .FromException(ex);
            }

            if (monadicPage.Failed)
            {
                CosmosException cosmosException = ExceptionToCosmosException.CreateFromException(monadicPage.Exception);
                if (!IsRetriableException(cosmosException))
                {
                    this.hasMoreResults = false;
                }

                return(new ResponseMessage(
                           statusCode: cosmosException.StatusCode,
                           requestMessage: null,
                           headers: cosmosException.Headers,
                           cosmosException: cosmosException,
                           diagnostics: cosmosException.DiagnosticsContext));
            }

            ReadFeedPage readFeedPage = monadicPage.Result;

            if (readFeedPage.State == default)
            {
                this.hasMoreResults = false;
            }

            // Make the continuation token match the older format:
            string continuationToken;

            if (readFeedPage.State != null)
            {
                List <CompositeContinuationToken> compositeContinuationTokens = new List <CompositeContinuationToken>();
                CosmosArray compositeContinuationTokensCosmosArray            = (CosmosArray)readFeedPage.State.ContinuationToken;
                foreach (CosmosElement arrayItem in compositeContinuationTokensCosmosArray)
                {
                    ReadFeedContinuationToken readFeedContinuationToken = ReadFeedContinuationToken.MonadicConvertFromCosmosElement(arrayItem).Result;
                    FeedRangeEpk  feedRangeEpk  = (FeedRangeEpk)readFeedContinuationToken.Range;
                    ReadFeedState readFeedState = readFeedContinuationToken.State;
                    CompositeContinuationToken compositeContinuationToken = new CompositeContinuationToken()
                    {
                        Range = feedRangeEpk.Range,
                        Token = readFeedState.ContinuationToken.ToString(),
                    };

                    compositeContinuationTokens.Add(compositeContinuationToken);
                }

                FeedRangeCompositeContinuation feedRangeCompositeContinuation = new FeedRangeCompositeContinuation(
                    containerRid: string.Empty,
                    feedRange: FeedRangeEpk.FullRange,
                    compositeContinuationTokens);

                continuationToken = feedRangeCompositeContinuation.ToString();
            }
            else
            {
                continuationToken = null;
            }

            return(new ResponseMessage(
                       statusCode: System.Net.HttpStatusCode.OK,
                       requestMessage: default,