Ejemplo n.º 1
0
 public ChangeFeedNotModifiedPage(
     double requestCharge,
     string activityId,
     ChangeFeedState state)
     : base(requestCharge, activityId, state)
 {
 }
Ejemplo n.º 2
0
        public ChangeFeedEntry(
            long sequence,
            DateTimeOffset timestamp,
            ChangeFeedAction action,
            string studyInstanceUid,
            string seriesInstanceUid,
            string sopInstanceUid,
            long originalVersion,
            long?currentVersion,
            ChangeFeedState state)
        {
            EnsureArg.IsNotNull(studyInstanceUid);
            EnsureArg.IsNotNull(seriesInstanceUid);
            EnsureArg.IsNotNull(sopInstanceUid);

            Sequence          = sequence;
            StudyInstanceUid  = studyInstanceUid;
            SeriesInstanceUid = seriesInstanceUid;
            SopInstanceUid    = sopInstanceUid;
            Action            = action;
            Timestamp         = timestamp;
            State             = state;
            OriginalVersion   = originalVersion;
            CurrentVersion    = currentVersion;
        }
Ejemplo n.º 3
0
        public async Task SomeChangesAsync()
        {
            IDocumentContainer documentContainer = await CreateDocumentContainerAsync(numItems : 1);

            CrossPartitionChangeFeedAsyncEnumerator enumerator = CrossPartitionChangeFeedAsyncEnumerator.Create(
                documentContainer,
                ChangeFeedMode.Incremental,
                new ChangeFeedRequestOptions(),
                new CrossFeedRangeState <ChangeFeedState>(
                    new FeedRangeState <ChangeFeedState>[]
            {
                new FeedRangeState <ChangeFeedState>(FeedRangeEpk.FullRange, ChangeFeedState.Beginning())
            }),
                cancellationToken: default);

            // First page should be true and skip the 304 not modified
            Assert.IsTrue(await enumerator.MoveNextAsync());
            Assert.IsTrue(enumerator.Current.Succeeded);
            Assert.IsTrue(enumerator.Current.Result.Page is ChangeFeedSuccessPage);

            // Second page should surface up the 304
            Assert.IsTrue(await enumerator.MoveNextAsync());
            Assert.IsTrue(enumerator.Current.Succeeded);
            Assert.IsTrue(enumerator.Current.Result.Page is ChangeFeedNotModifiedPage);
        }
        public async Task StartFromTimeAsync(bool useContinuations)
        {
            int numItems = 100;
            IDocumentContainer documentContainer = await CreateDocumentContainerAsync(numItems);
            CrossPartitionChangeFeedAsyncEnumerator enumerator = CrossPartitionChangeFeedAsyncEnumerator.Create(
                documentContainer,
                new CrossFeedRangeState<ChangeFeedState>(
                    new FeedRangeState<ChangeFeedState>[]
                    {
                        new FeedRangeState<ChangeFeedState>(FeedRangeEpk.FullRange, ChangeFeedState.Time(DateTime.UtcNow))
                    }),
                ChangeFeedPaginationOptions.Default,
                cancellationToken: default);

            for (int i = 0; i < numItems; i++)
            {
                // Insert an item
                CosmosObject item = CosmosObject.Parse($"{{\"pk\" : {i} }}");
                while (true)
                {
                    TryCatch<Record> monadicCreateRecord = await documentContainer.MonadicCreateItemAsync(item, cancellationToken: default);
                    if (monadicCreateRecord.Succeeded)
                    {
                        break;
                    }
                }
            }

            (int globalCount, double _) = await (useContinuations
                ? DrainWithUntilNotModifiedWithContinuationTokens(documentContainer, enumerator)
                : DrainUntilNotModifedAsync(enumerator));

            Assert.AreEqual(numItems, globalCount);
        }
Ejemplo n.º 5
0
        public async Task StartFromBeginingTestAsync()
        {
            IDocumentContainer documentContainer = await this.CreateDocumentContainerAsync(numItems : 10);

            List <FeedRangeEpk> ranges = await documentContainer.GetFeedRangesAsync(NoOpTrace.Singleton, cancellationToken : default);

            // Should get back the all the documents inserted so far
            ChangeFeedState resumeState;
            {
                TryCatch <ChangeFeedPage> monadicChangeFeedPage = await documentContainer.MonadicChangeFeedAsync(
                    ChangeFeedState.Beginning(),
                    ranges[0],
                    pageSize : int.MaxValue,
                    trace : NoOpTrace.Singleton,
                    cancellationToken : default);

                Assert.IsTrue(monadicChangeFeedPage.Succeeded);

                resumeState = monadicChangeFeedPage.Result.State;
            }

            // No more changes left
            {
                TryCatch <ChangeFeedPage> monadicChangeFeedPage = await documentContainer.MonadicChangeFeedAsync(
                    resumeState,
                    ranges[0],
                    pageSize : 10,
                    trace : NoOpTrace.Singleton,
                    cancellationToken : default);

                Assert.IsTrue(monadicChangeFeedPage.Succeeded);
                Assert.IsTrue(monadicChangeFeedPage.Result is ChangeFeedNotModifiedPage);
            }
        }
Ejemplo n.º 6
0
        public ChangeFeedEntry(
            long sequence,
            DateTimeOffset timestamp,
            ChangeFeedAction action,
            string studyInstanceUid,
            string seriesInstanceUid,
            string sopInstanceUid,
            ChangeFeedState state,
            string partitionName  = default,
            DicomDataset metadata = null)
        {
            EnsureArg.IsNotNull(studyInstanceUid);
            EnsureArg.IsNotNull(seriesInstanceUid);
            EnsureArg.IsNotNull(sopInstanceUid);

            Sequence          = sequence;
            StudyInstanceUid  = studyInstanceUid;
            SeriesInstanceUid = seriesInstanceUid;
            SopInstanceUid    = sopInstanceUid;
            Action            = action;
            Timestamp         = timestamp;
            State             = state;
            PartitionName     = partitionName;
            Metadata          = metadata;
        }
Ejemplo n.º 7
0
        public void Now()
        {
            ChangeFeedState            now           = ChangeFeedState.Now();
            CosmosElement              cosmosElement = ChangeFeedStateCosmosElementSerializer.ToCosmosElement(now);
            TryCatch <ChangeFeedState> monadicState  = ChangeFeedStateCosmosElementSerializer.MonadicFromCosmosElement(cosmosElement);

            Assert.IsTrue(monadicState.Succeeded);
            Assert.IsTrue(monadicState.Result is ChangeFeedStateNow);
        }
Ejemplo n.º 8
0
 public Task <TryCatch <ChangeFeedPage> > MonadicChangeFeedAsync(
     ChangeFeedState state,
     FeedRangeInternal feedRange,
     int pageSize,
     CancellationToken cancellationToken) => this.monadicDocumentContainer.MonadicChangeFeedAsync(
     state,
     feedRange,
     pageSize,
     cancellationToken);
Ejemplo n.º 9
0
        public void Beginning()
        {
            ChangeFeedState            beginning     = ChangeFeedState.Beginning();
            CosmosElement              cosmosElement = ChangeFeedStateCosmosElementSerializer.ToCosmosElement(beginning);
            TryCatch <ChangeFeedState> monadicState  = ChangeFeedStateCosmosElementSerializer.MonadicFromCosmosElement(cosmosElement);

            Assert.IsTrue(monadicState.Succeeded);
            Assert.IsTrue(monadicState.Result is ChangeFeedStateBeginning);
        }
Ejemplo n.º 10
0
        public async Task EmptyContainerTestAsync()
        {
            IDocumentContainer documentContainer = await this.CreateDocumentContainerAsync(numItems : 0);

            List <FeedRangeEpk> ranges = await documentContainer.GetFeedRangesAsync(NoOpTrace.Singleton, cancellationToken : default);

            TryCatch <ChangeFeedPage> monadicChangeFeedPage = await documentContainer.MonadicChangeFeedAsync(
                feedRangeState : new FeedRangeState <ChangeFeedState>(ranges[0], ChangeFeedState.Beginning()),
                changeFeedPaginationOptions : new ChangeFeedPaginationOptions(ChangeFeedMode.Incremental, pageSizeHint: 10),
                trace : NoOpTrace.Singleton,
                cancellationToken : default);
Ejemplo n.º 11
0
 public Task <ChangeFeedPage> ChangeFeedAsync(
     ChangeFeedState state,
     FeedRangeInternal feedRange,
     int pageSize,
     CancellationToken cancellationToken) => TryCatch <ChangeFeedPage> .UnsafeGetResultAsync(
     this.MonadicChangeFeedAsync(
         state,
         feedRange,
         pageSize,
         cancellationToken),
     cancellationToken);
Ejemplo n.º 12
0
        public static ChangeFeedCrossFeedRangeState CreateFromContinuation(CosmosElement continuation, FeedRange feedRange)
        {
            if (!(feedRange is FeedRangeInternal feedRangeInternal))
            {
                throw new ArgumentException($"{nameof(feedRange)} needs to be a {nameof(FeedRangeInternal)}.");
            }

            return(new ChangeFeedCrossFeedRangeState(
                       new List <FeedRangeState <ChangeFeedState> >()
            {
                new FeedRangeState <ChangeFeedState>(feedRangeInternal, ChangeFeedState.Continuation(continuation))
            }));
        }
Ejemplo n.º 13
0
        public static ChangeFeedCrossFeedRangeState CreateFromTime(DateTime dateTimeUtc, FeedRange feedRange)
        {
            if (!(feedRange is FeedRangeInternal feedRangeInternal))
            {
                throw new ArgumentException($"{nameof(feedRange)} needs to be a {nameof(FeedRangeInternal)}.");
            }

            return(new ChangeFeedCrossFeedRangeState(
                       new List <FeedRangeState <ChangeFeedState> >()
            {
                new FeedRangeState <ChangeFeedState>(feedRangeInternal, ChangeFeedState.Time(dateTimeUtc))
            }));
        }
Ejemplo n.º 14
0
        public async Task MonadicChangeFeedAsync_ChangeFeedMode_Incremental()
        {
            Mock <ContainerInternal>   container = new Mock <ContainerInternal>();
            Mock <CosmosClientContext> context   = new Mock <CosmosClientContext>();

            container.Setup(m => m.ClientContext).Returns(context.Object);

            Func <Action <RequestMessage>, bool> validateEnricher = (Action <RequestMessage> enricher) =>
            {
                RequestMessage requestMessage = new RequestMessage();
                enricher(requestMessage);
                Assert.AreEqual(HttpConstants.A_IMHeaderValues.IncrementalFeed, requestMessage.Headers[HttpConstants.HttpHeaders.A_IM]);
                return(true);
            };

            ResponseMessage response = new ResponseMessage(System.Net.HttpStatusCode.NotModified);

            response.Headers.ETag          = Guid.NewGuid().ToString();
            response.Headers.ActivityId    = Guid.NewGuid().ToString();
            response.Headers.RequestCharge = 1;

            context.SetupSequence(c => c.ProcessResourceOperationStreamAsync(
                                      It.IsAny <string>(),
                                      It.Is <ResourceType>(rt => rt == ResourceType.Document),
                                      It.Is <OperationType>(rt => rt == OperationType.ReadFeed),
                                      It.IsAny <RequestOptions>(),
                                      It.Is <ContainerInternal>(o => o == container.Object),
                                      It.IsAny <FeedRangeInternal>(),
                                      It.IsAny <Stream>(),
                                      It.Is <Action <RequestMessage> >(enricher => validateEnricher(enricher)),
                                      It.IsAny <CosmosDiagnosticsContext>(),
                                      It.IsAny <ITrace>(),
                                      It.IsAny <CancellationToken>()
                                      )
                                  ).ReturnsAsync(response);

            NetworkAttachedDocumentContainer networkAttachedDocumentContainer = new NetworkAttachedDocumentContainer(
                container.Object,
                Mock.Of <CosmosQueryClient>(),
                Mock.Of <CosmosDiagnosticsContext>());

            await networkAttachedDocumentContainer.MonadicChangeFeedAsync(
                state : ChangeFeedState.Beginning(),
                feedRange : new FeedRangePartitionKeyRange("0"),
                pageSize : 10,
                changeFeedMode : ChangeFeedMode.Incremental,
                jsonSerializationFormat : null,
                trace : NoOpTrace.Singleton,
                cancellationToken : default);
Ejemplo n.º 15
0
 public Task <TryCatch <ChangeFeedPage> > MonadicChangeFeedAsync(
     ChangeFeedState state,
     FeedRangeInternal feedRange,
     int pageSize,
     ChangeFeedMode changeFeedMode,
     JsonSerializationFormat?jsonSerializationFormat,
     ITrace trace,
     CancellationToken cancellationToken) => this.monadicDocumentContainer.MonadicChangeFeedAsync(
     state,
     feedRange,
     pageSize,
     changeFeedMode,
     jsonSerializationFormat,
     trace,
     cancellationToken);
        public async Task TargetMultipleLogicalPartitionKeys()
        {
            int batchSize = 25;

            string pkToRead1 = "pkToRead1";
            string pkToRead2 = "pkToRead2";
            string otherPK   = "otherPK";

            for (int i = 0; i < batchSize; i++)
            {
                await this.Container.CreateItemAsync(this.CreateRandomToDoActivity(pkToRead1));
            }

            for (int i = 0; i < batchSize; i++)
            {
                await this.Container.CreateItemAsync(this.CreateRandomToDoActivity(pkToRead2));
            }

            for (int i = 0; i < batchSize; i++)
            {
                await this.Container.CreateItemAsync(this.CreateRandomToDoActivity(otherPK));
            }

            // Create one start state for each logical partition key.
            List <FeedRangeState <ChangeFeedState> > feedRangeStates = new List <FeedRangeState <ChangeFeedState> >();
            IReadOnlyList <string> partitionKeysToTarget             = new List <string>()
            {
                pkToRead1,
                pkToRead2
            };

            foreach (string partitionKeyToTarget in partitionKeysToTarget)
            {
                feedRangeStates.Add(
                    new FeedRangeState <ChangeFeedState>(
                        (FeedRangeInternal)FeedRange.FromPartitionKey(
                            new Cosmos.PartitionKey(partitionKeyToTarget)),
                        ChangeFeedState.Beginning()));
            }

            // Use the list composition property of the constructor to merge them in to a single state.
            ChangeFeedCrossFeedRangeState multipleLogicalPartitionKeyState = new ChangeFeedCrossFeedRangeState(feedRangeStates.ToImmutableArray());
            IAsyncEnumerable <TryCatch <ChangeFeedPage> > asyncEnumerable  = this.Container.GetChangeFeedAsyncEnumerable(multipleLogicalPartitionKeyState, ChangeFeedMode.Incremental);

            (int totalCount, ChangeFeedCrossFeedRangeState _) = await PartialDrainAsync(asyncEnumerable);

            Assert.AreEqual(2 * batchSize, totalCount);
        }
Ejemplo n.º 17
0
        public void Time()
        {
            DateTime                   startTime     = DateTime.MinValue.ToUniversalTime();
            ChangeFeedState            time          = ChangeFeedState.Time(startTime);
            CosmosElement              cosmosElement = ChangeFeedStateCosmosElementSerializer.ToCosmosElement(time);
            TryCatch <ChangeFeedState> monadicState  = ChangeFeedStateCosmosElementSerializer.MonadicFromCosmosElement(cosmosElement);

            Assert.IsTrue(monadicState.Succeeded);
            if (!(monadicState.Result is ChangeFeedStateTime stateTime))
            {
                Assert.Fail();
                return;
            }

            Assert.AreEqual(stateTime.StartTime, startTime);
        }
Ejemplo n.º 18
0
        public async Task EmptyContainerTestAsync()
        {
            IDocumentContainer documentContainer = await this.CreateDocumentContainerAsync(numItems : 0);

            List <FeedRangeEpk> ranges = await documentContainer.GetFeedRangesAsync(NoOpTrace.Singleton, cancellationToken : default);

            TryCatch <ChangeFeedPage> monadicChangeFeedPage = await documentContainer.MonadicChangeFeedAsync(
                ChangeFeedState.Beginning(),
                ranges[0],
                pageSize : 10,
                trace : NoOpTrace.Singleton,
                cancellationToken : default);

            Assert.IsTrue(monadicChangeFeedPage.Succeeded);
            Assert.IsTrue(monadicChangeFeedPage.Result is ChangeFeedNotModifiedPage);
        }
Ejemplo n.º 19
0
        public void Continuation()
        {
            CosmosString               continuation  = CosmosString.Create("asdf");
            ChangeFeedState            time          = ChangeFeedState.Continuation(continuation);
            CosmosElement              cosmosElement = ChangeFeedStateCosmosElementSerializer.ToCosmosElement(time);
            TryCatch <ChangeFeedState> monadicState  = ChangeFeedStateCosmosElementSerializer.MonadicFromCosmosElement(cosmosElement);

            Assert.IsTrue(monadicState.Succeeded);
            if (!(monadicState.Result is ChangeFeedStateContinuation changeFeedContinuation))
            {
                Assert.Fail();
                return;
            }

            Assert.AreEqual(changeFeedContinuation.ContinuationToken, continuation);
        }
Ejemplo n.º 20
0
 public Task <ChangeFeedPage> ChangeFeedAsync(
     ChangeFeedState state,
     FeedRangeInternal feedRange,
     int pageSize,
     ChangeFeedMode changeFeedMode,
     JsonSerializationFormat?jsonSerializationFormat,
     ITrace trace,
     CancellationToken cancellationToken) => TryCatch <ChangeFeedPage> .UnsafeGetResultAsync(
     this.MonadicChangeFeedAsync(
         state,
         feedRange,
         pageSize,
         changeFeedMode,
         jsonSerializationFormat,
         trace,
         cancellationToken),
     cancellationToken);
        public async Task NoChangesAsync()
        {
            IDocumentContainer documentContainer = await CreateDocumentContainerAsync(numItems: 0);
            CrossPartitionChangeFeedAsyncEnumerator enumerator = CrossPartitionChangeFeedAsyncEnumerator.Create(
                documentContainer,
                new CrossFeedRangeState<ChangeFeedState>(
                    new FeedRangeState<ChangeFeedState>[]
                    {
                        new FeedRangeState<ChangeFeedState>(FeedRangeEpk.FullRange, ChangeFeedState.Beginning())
                    }),
                ChangeFeedPaginationOptions.Default,
                cancellationToken: default);

            Assert.IsTrue(await enumerator.MoveNextAsync());
            Assert.IsTrue(enumerator.Current.Succeeded);
            Assert.IsTrue(enumerator.Current.Result.Page is ChangeFeedNotModifiedPage);
            Assert.IsNotNull(enumerator.Current.Result.State);
        }
Ejemplo n.º 22
0
        public static ChangeFeedCrossFeedRangeState CreateFromNow(FeedRange feedRange)
        {
            if (!(feedRange is FeedRangeInternal feedRangeInternal))
            {
                throw new ArgumentException($"{nameof(feedRange)} needs to be a {nameof(FeedRangeInternal)}.");
            }

            if (feedRange.Equals(FeedRangeEpk.FullRange))
            {
                return(FullRangeStatesSingletons.Now);
            }

            return(new ChangeFeedCrossFeedRangeState(
                       new List <FeedRangeState <ChangeFeedState> >()
            {
                new FeedRangeState <ChangeFeedState>(feedRangeInternal, ChangeFeedState.Now())
            }));
        }
        public async Task StartFromBeginningAsync(bool useContinuations)
        {
            int numItems = 100;
            IDocumentContainer documentContainer = await CreateDocumentContainerAsync(numItems);
            CrossPartitionChangeFeedAsyncEnumerator enumerator = CrossPartitionChangeFeedAsyncEnumerator.Create(
                documentContainer,
                new CrossFeedRangeState<ChangeFeedState>(
                    new FeedRangeState<ChangeFeedState>[]
                    {
                        new FeedRangeState<ChangeFeedState>(FeedRangeEpk.FullRange, ChangeFeedState.Beginning())
                    }),
                ChangeFeedPaginationOptions.Default,
                cancellationToken: default);

            (int globalCount, double _) = await (useContinuations
                ? DrainWithUntilNotModifiedWithContinuationTokens(documentContainer, enumerator)
                : DrainUntilNotModifedAsync(enumerator));
            Assert.AreEqual(numItems, globalCount);
        }
Ejemplo n.º 24
0
        public ChangeFeedEntry(
            long sequence,
            DateTime timestamp,
            ChangeFeedAction action,
            string studyInstanceUid,
            string seriesInstanceUid,
            string sopInstanceUid,
            ChangeFeedState state)
        {
            EnsureArg.IsNotNull(studyInstanceUid);
            EnsureArg.IsNotNull(seriesInstanceUid);
            EnsureArg.IsNotNull(sopInstanceUid);

            Sequence          = sequence;
            StudyInstanceUid  = studyInstanceUid;
            SeriesInstanceUid = seriesInstanceUid;
            SopInstanceUid    = sopInstanceUid;
            Action            = action;
            Timestamp         = timestamp;
            State             = state;
        }
        public async Task ShouldReturnNotModifiedAfterCyclingOnAllRanges(int partitions)
        {
            ReadOnlyMemory<FeedRangeState<ChangeFeedState>> rangeStates = null;

            if (partitions == 1)
            {
                rangeStates = new FeedRangeState<ChangeFeedState>[]{
                    new FeedRangeState<ChangeFeedState>(FeedRangeEpk.FullRange, ChangeFeedState.Now())
                };
            }
            if (partitions == 2)
            {
                rangeStates = new FeedRangeState<ChangeFeedState>[]{
                    new FeedRangeState<ChangeFeedState>(new FeedRangeEpk(new Documents.Routing.Range<string>("", "AA", true, false)), ChangeFeedState.Now()),
                    new FeedRangeState<ChangeFeedState>(new FeedRangeEpk(new Documents.Routing.Range<string>("AA", "FF", true, false)), ChangeFeedState.Now()),
                };
            }
            if (partitions == 3)
            {
                rangeStates = new FeedRangeState<ChangeFeedState>[]{
                    new FeedRangeState<ChangeFeedState>(new FeedRangeEpk(new Documents.Routing.Range<string>("", "AA", true, false)), ChangeFeedState.Now()),
                    new FeedRangeState<ChangeFeedState>(new FeedRangeEpk(new Documents.Routing.Range<string>("AA", "BB", true, false)), ChangeFeedState.Now()),
                    new FeedRangeState<ChangeFeedState>(new FeedRangeEpk(new Documents.Routing.Range<string>("BB", "FF", true, false)), ChangeFeedState.Now()),
                };
            }

            Assert.IsNotNull(rangeStates, $"Range states not initialized for {partitions} partitions");

            CrossFeedRangeState<ChangeFeedState> state = new CrossFeedRangeState<ChangeFeedState>(rangeStates);
            Mock<IDocumentContainer> documentContainer = new Mock<IDocumentContainer>();

            // Returns a 304 with 1RU charge
            documentContainer.Setup(c => c.MonadicChangeFeedAsync(
                It.IsAny<FeedRangeState<ChangeFeedState>>(),
                It.IsAny<ChangeFeedPaginationOptions>(),
                It.IsAny<ITrace>(),
                It.IsAny<CancellationToken>())).ReturnsAsync(
                (FeedRangeState<ChangeFeedState> state, ChangeFeedPaginationOptions options, ITrace trace, CancellationToken token) 
                    => TryCatch<ChangeFeedPage>.FromResult(new ChangeFeedNotModifiedPage(requestCharge: 1, activityId: string.Empty, additionalHeaders: default, state.State)));
Ejemplo n.º 26
0
        public ChangeFeedIteratorCore(
            IDocumentContainer documentContainer,
            ChangeFeedRequestOptions changeFeedRequestOptions,
            ChangeFeedStartFrom changeFeedStartFrom)
        {
            if (changeFeedStartFrom == null)
            {
                throw new ArgumentNullException(nameof(changeFeedStartFrom));
            }

            this.documentContainer        = documentContainer ?? throw new ArgumentNullException(nameof(documentContainer));
            this.changeFeedRequestOptions = changeFeedRequestOptions ?? new ChangeFeedRequestOptions();
            this.lazyMonadicEnumerator    = new AsyncLazy <TryCatch <CrossPartitionChangeFeedAsyncEnumerator> >(
                valueFactory: async(cancellationToken) =>
            {
                if (changeFeedStartFrom is ChangeFeedStartFromContinuation startFromContinuation)
                {
                    TryCatch <CosmosElement> monadicParsedToken = CosmosElement.Monadic.Parse(startFromContinuation.Continuation);
                    if (monadicParsedToken.Failed)
                    {
                        return(TryCatch <CrossPartitionChangeFeedAsyncEnumerator> .FromException(
                                   new MalformedChangeFeedContinuationTokenException(
                                       message: $"Failed to parse continuation token: {startFromContinuation.Continuation}.",
                                       innerException: monadicParsedToken.Exception)));
                    }

                    TryCatch <VersionedAndRidCheckedCompositeToken> monadicVersionedToken = VersionedAndRidCheckedCompositeToken
                                                                                            .MonadicCreateFromCosmosElement(monadicParsedToken.Result);
                    if (monadicVersionedToken.Failed)
                    {
                        return(TryCatch <CrossPartitionChangeFeedAsyncEnumerator> .FromException(
                                   new MalformedChangeFeedContinuationTokenException(
                                       message: $"Failed to parse continuation token: {startFromContinuation.Continuation}.",
                                       innerException: monadicVersionedToken.Exception)));
                    }

                    VersionedAndRidCheckedCompositeToken versionedAndRidCheckedCompositeToken = monadicVersionedToken.Result;
                    if (versionedAndRidCheckedCompositeToken.VersionNumber == VersionedAndRidCheckedCompositeToken.Version.V1)
                    {
                        // Need to migrate continuation token
                        if (!(versionedAndRidCheckedCompositeToken.ContinuationToken is CosmosArray cosmosArray))
                        {
                            return(TryCatch <CrossPartitionChangeFeedAsyncEnumerator> .FromException(
                                       new MalformedChangeFeedContinuationTokenException(
                                           message: $"Failed to parse get array continuation token: {startFromContinuation.Continuation}.")));
                        }

                        List <CosmosElement> changeFeedTokensV2 = new List <CosmosElement>();
                        foreach (CosmosElement arrayItem in cosmosArray)
                        {
                            if (!(arrayItem is CosmosObject cosmosObject))
                            {
                                return(TryCatch <CrossPartitionChangeFeedAsyncEnumerator> .FromException(
                                           new MalformedChangeFeedContinuationTokenException(
                                               message: $"Failed to parse get object in composite continuation: {startFromContinuation.Continuation}.")));
                            }

                            if (!cosmosObject.TryGetValue("min", out CosmosString min))
                            {
                                return(TryCatch <CrossPartitionChangeFeedAsyncEnumerator> .FromException(
                                           new MalformedChangeFeedContinuationTokenException(
                                               message: $"Failed to parse start of range: {cosmosObject}.")));
                            }

                            if (!cosmosObject.TryGetValue("max", out CosmosString max))
                            {
                                return(TryCatch <CrossPartitionChangeFeedAsyncEnumerator> .FromException(
                                           new MalformedChangeFeedContinuationTokenException(
                                               message: $"Failed to parse end of range: {cosmosObject}.")));
                            }

                            if (!cosmosObject.TryGetValue("token", out CosmosElement token))
                            {
                                return(TryCatch <CrossPartitionChangeFeedAsyncEnumerator> .FromException(
                                           new MalformedChangeFeedContinuationTokenException(
                                               message: $"Failed to parse token: {cosmosObject}.")));
                            }

                            FeedRangeEpk feedRangeEpk = new FeedRangeEpk(new Documents.Routing.Range <string>(
                                                                             min: min.Value,
                                                                             max: max.Value,
                                                                             isMinInclusive: true,
                                                                             isMaxInclusive: false));
                            ChangeFeedState state = token is CosmosNull ? ChangeFeedState.Beginning() : ChangeFeedStateContinuation.Continuation(token);

                            FeedRangeState <ChangeFeedState> feedRangeState = new FeedRangeState <ChangeFeedState>(feedRangeEpk, state);
                            changeFeedTokensV2.Add(ChangeFeedFeedRangeStateSerializer.ToCosmosElement(feedRangeState));
                        }

                        CosmosArray changeFeedTokensArrayV2 = CosmosArray.Create(changeFeedTokensV2);

                        versionedAndRidCheckedCompositeToken = new VersionedAndRidCheckedCompositeToken(
                            VersionedAndRidCheckedCompositeToken.Version.V2,
                            changeFeedTokensArrayV2,
                            versionedAndRidCheckedCompositeToken.Rid);
                    }

                    if (versionedAndRidCheckedCompositeToken.VersionNumber != VersionedAndRidCheckedCompositeToken.Version.V2)
                    {
                        return(TryCatch <CrossPartitionChangeFeedAsyncEnumerator> .FromException(
                                   new MalformedChangeFeedContinuationTokenException(
                                       message: $"Wrong version number: {versionedAndRidCheckedCompositeToken.VersionNumber}.")));
                    }

                    string collectionRid = await documentContainer.GetResourceIdentifierAsync(cancellationToken);
                    if (versionedAndRidCheckedCompositeToken.Rid != collectionRid)
                    {
                        return(TryCatch <CrossPartitionChangeFeedAsyncEnumerator> .FromException(
                                   new MalformedChangeFeedContinuationTokenException(
                                       message: $"rids mismatched. Expected: {collectionRid} but got {versionedAndRidCheckedCompositeToken.Rid}.")));
                    }

                    changeFeedStartFrom = ChangeFeedStartFrom.ContinuationToken(versionedAndRidCheckedCompositeToken.ContinuationToken.ToString());
                }

                TryCatch <ChangeFeedCrossFeedRangeState> monadicChangeFeedCrossFeedRangeState = changeFeedStartFrom.Accept(ChangeFeedStateFromToChangeFeedCrossFeedRangeState.Singleton);
                if (monadicChangeFeedCrossFeedRangeState.Failed)
                {
                    return(TryCatch <CrossPartitionChangeFeedAsyncEnumerator> .FromException(
                               new MalformedChangeFeedContinuationTokenException(
                                   message: $"Could not convert to {nameof(ChangeFeedCrossFeedRangeState)}.",
                                   innerException: monadicChangeFeedCrossFeedRangeState.Exception)));
                }

                CrossPartitionChangeFeedAsyncEnumerator enumerator = CrossPartitionChangeFeedAsyncEnumerator.Create(
                    documentContainer,
                    changeFeedRequestOptions,
                    new CrossFeedRangeState <ChangeFeedState>(monadicChangeFeedCrossFeedRangeState.Result.FeedRangeStates),
                    cancellationToken: default);

                TryCatch <CrossPartitionChangeFeedAsyncEnumerator> monadicEnumerator = TryCatch <CrossPartitionChangeFeedAsyncEnumerator> .FromResult(enumerator);
                return(monadicEnumerator);
            });
            this.hasMoreResults = true;
        }