public override async Task <TryCatch <QueryPage> > MonadicQueryAsync( SqlQuerySpec sqlQuerySpec, FeedRangeState <QueryState> feedRangeState, QueryPaginationOptions queryPaginationOptions, ITrace trace, CancellationToken cancellationToken) { await this.semaphore.WaitAsync(this.cancellationToken); int count = ParseQueryState(feedRangeState.State); QueryState continuationToken = count < this.continuationCount ? CreateQueryState(++count) : default; QueryPage page = new QueryPage( documents: new List <CosmosElement> { }, requestCharge: 3.0, activityId: "E7980B1F-436E-44DF-B7A5-655C56D38648", responseLengthInBytes: 48, cosmosQueryExecutionInfo: new Lazy <CosmosQueryExecutionInfo>(() => new CosmosQueryExecutionInfo(false, false)), disallowContinuationTokenMessage: null, additionalHeaders: null, state: continuationToken); return(continuationToken != default ? TryCatch <QueryPage> .FromResult(page) : await base.MonadicQueryAsync( sqlQuerySpec, new FeedRangeState <QueryState>(feedRangeState.FeedRange, default), queryPaginationOptions, trace, cancellationToken)); }
internal static TryCatch <ReadFeedCrossFeedRangeState> CreateFromCosmosElement(CosmosElement cosmosElement) { if (cosmosElement == null) { throw new ArgumentNullException(nameof(cosmosElement)); } if (!(cosmosElement is CosmosArray cosmosArray)) { return(TryCatch <ReadFeedCrossFeedRangeState> .FromException( new FormatException( $"Expected array: {cosmosElement}"))); } FeedRangeState <ReadFeedState>[] feedRangeStates = new FeedRangeState <ReadFeedState> [cosmosArray.Count]; int i = 0; foreach (CosmosElement arrayItem in cosmosArray) { TryCatch <FeedRangeState <ReadFeedState> > monadicFeedRangeState = ReadFeedFeedRangeStateSerializer.Monadic.CreateFromCosmosElement(arrayItem); if (monadicFeedRangeState.Failed) { return(TryCatch <ReadFeedCrossFeedRangeState> .FromException(monadicFeedRangeState.Exception)); } feedRangeStates[i++] = monadicFeedRangeState.Result; } ReadFeedCrossFeedRangeState crossFeedRangeState = new ReadFeedCrossFeedRangeState(feedRangeStates.AsMemory()); return(TryCatch <ReadFeedCrossFeedRangeState> .FromResult(crossFeedRangeState)); }
public static CosmosElement ToCosmosElement(FeedRangeState <ReadFeedState> feedRangeState) { Dictionary <string, CosmosElement> dictionary = new Dictionary <string, CosmosElement>() { { PropertyNames.FeedRange, FeedRangeCosmosElementSerializer.ToCosmosElement(feedRangeState.FeedRange) } }; if (feedRangeState.State is ReadFeedBeginningState) { dictionary[PropertyNames.State] = CosmosNull.Create(); } else if (feedRangeState.State is ReadFeedContinuationState readFeedContinuationState) { dictionary[PropertyNames.State] = readFeedContinuationState.ContinuationToken; } else { throw new InvalidOperationException("Unknown FeedRange State."); } return(CosmosObject.Create(dictionary)); }
public PartitionRangePageAsyncEnumerable( FeedRangeState <TState> feedRangeState, CreatePartitionRangePageAsyncEnumerator <TPage, TState> createPartitionRangeEnumerator, ITrace trace) { this.feedRangeState = feedRangeState; this.createPartitionRangeEnumerator = createPartitionRangeEnumerator ?? throw new ArgumentNullException(nameof(createPartitionRangeEnumerator)); this.trace = trace ?? throw new ArgumentNullException(nameof(trace)); }
public ReadFeedPartitionRangeEnumerator( IReadFeedDataSource readFeedDataSource, FeedRangeState <ReadFeedState> feedRangeState, ReadFeedPaginationOptions readFeedPaginationOptions, CancellationToken cancellationToken) : base(feedRangeState, cancellationToken) { this.readFeedDataSource = readFeedDataSource ?? throw new ArgumentNullException(nameof(readFeedDataSource)); this.readFeedPaginationOptions = readFeedPaginationOptions; }
public ChangeFeedPartitionRangePageAsyncEnumerator( IChangeFeedDataSource changeFeedDataSource, FeedRangeState <ChangeFeedState> feedRangeState, ChangeFeedPaginationOptions changeFeedPaginationOptions, CancellationToken cancellationToken) : base(feedRangeState, cancellationToken) { this.changeFeedDataSource = changeFeedDataSource ?? throw new ArgumentNullException(nameof(changeFeedDataSource)); this.changeFeedPaginationOptions = changeFeedPaginationOptions ?? throw new ArgumentNullException(nameof(changeFeedPaginationOptions)); }
public async ValueTask <bool> MoveNextAsync() { this.cancellationToken.ThrowIfCancellationRequested(); if (!await this.crossPartitionEnumerator.MoveNextAsync()) { this.Current = default; return(false); } TryCatch <CrossFeedRangePage <ReadFeedPage, ReadFeedState> > monadicCrossPartitionPage = this.crossPartitionEnumerator.Current; if (monadicCrossPartitionPage.Failed) { this.Current = TryCatch <ReadFeedPage> .FromException(monadicCrossPartitionPage.Exception); return(true); } CrossFeedRangePage <ReadFeedPage, ReadFeedState> crossPartitionPage = monadicCrossPartitionPage.Result; ReadFeedPage backendPage = crossPartitionPage.Page; CrossFeedRangeState <ReadFeedState> crossPartitionState = crossPartitionPage.State; ReadFeedState state; if (crossPartitionState != null) { List <CosmosElement> changeFeedContinuationTokens = new List <CosmosElement>(); for (int i = 0; i < crossPartitionState.Value.Length; i++) { this.cancellationToken.ThrowIfCancellationRequested(); FeedRangeState <ReadFeedState> feedRangeState = crossPartitionState.Value.Span[i]; ReadFeedContinuationToken readFeedContinuationToken = new ReadFeedContinuationToken( feedRangeState.FeedRange, feedRangeState.State); CosmosElement cosmosElementChangeFeedContinuationToken = ReadFeedContinuationToken.ToCosmosElement(readFeedContinuationToken); changeFeedContinuationTokens.Add(cosmosElementChangeFeedContinuationToken); } CosmosArray cosmosElementTokens = CosmosArray.Create(changeFeedContinuationTokens); state = new ReadFeedState(cosmosElementTokens); } else { state = null; } ReadFeedPage compositePage = new ReadFeedPage(backendPage.Content, backendPage.RequestCharge, backendPage.ActivityId, backendPage.Diagnostics, state); this.Current = TryCatch <ReadFeedPage> .FromResult(compositePage); return(true); }
public QueryPartitionRangePageAsyncEnumerator( IQueryDataSource queryDataSource, SqlQuerySpec sqlQuerySpec, FeedRangeState <QueryState> feedRangeState, Cosmos.PartitionKey?partitionKey, QueryPaginationOptions queryPaginationOptions, CancellationToken cancellationToken) : base(feedRangeState, cancellationToken) { this.queryDataSource = queryDataSource ?? throw new ArgumentNullException(nameof(queryDataSource)); this.sqlQuerySpec = sqlQuerySpec ?? throw new ArgumentNullException(nameof(sqlQuerySpec)); this.queryPaginationOptions = queryPaginationOptions; this.partitionKey = partitionKey; }
public static CosmosElement ToCosmosElement(FeedRangeState <ChangeFeedState> feedRangeState) { return(CosmosObject.Create(new Dictionary <string, CosmosElement>() { { PropertyNames.FeedRange, FeedRangeCosmosElementSerializer.ToCosmosElement(feedRangeState.FeedRange) }, { PropertyNames.State, ChangeFeedStateCosmosElementSerializer.ToCosmosElement(feedRangeState.State) } })); }
public InnerEnumerator( IQueryDataSource queryDataSource, SqlQuerySpec sqlQuerySpec, FeedRangeState <QueryState> feedRangeState, PartitionKey?partitionKey, QueryPaginationOptions queryPaginationOptions, string filter, CancellationToken cancellationToken) : base(feedRangeState, cancellationToken) { this.queryDataSource = queryDataSource ?? throw new ArgumentNullException(nameof(queryDataSource)); this.SqlQuerySpec = sqlQuerySpec ?? throw new ArgumentNullException(nameof(sqlQuerySpec)); this.PartitionKey = partitionKey; this.queryPaginationOptions = queryPaginationOptions ?? QueryPaginationOptions.Default; this.Filter = filter; }
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)));
public OrderByQueryPartitionRangePageAsyncEnumerator( IQueryDataSource queryDataSource, SqlQuerySpec sqlQuerySpec, FeedRangeState <QueryState> feedRangeState, PartitionKey?partitionKey, QueryPaginationOptions queryPaginationOptions, string filter, CancellationToken cancellationToken) : base(feedRangeState, cancellationToken) { this.StartOfPageState = feedRangeState.State; this.innerEnumerator = new InnerEnumerator( queryDataSource, sqlQuerySpec, feedRangeState, partitionKey, queryPaginationOptions, filter, cancellationToken); this.bufferedEnumerator = new BufferedPartitionRangePageAsyncEnumerator <OrderByQueryPage, QueryState>( this.innerEnumerator, cancellationToken); }
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,
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; }
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); }
private static TryCatch <CrossFeedRangeState <QueryState> > MonadicExtractState( CosmosElement continuationToken, IReadOnlyList <FeedRangeEpk> ranges) { if (continuationToken == null) { // Full fan out to the ranges with null continuations CrossFeedRangeState <QueryState> fullFanOutState = new CrossFeedRangeState <QueryState>(ranges.Select(range => new FeedRangeState <QueryState>(range, (QueryState)null)).ToArray()); return(TryCatch <CrossFeedRangeState <QueryState> > .FromResult(fullFanOutState)); } if (!(continuationToken is CosmosArray parallelContinuationTokenListRaw)) { return(TryCatch <CrossFeedRangeState <QueryState> > .FromException( new MalformedContinuationTokenException( $"Invalid format for continuation token {continuationToken} for {nameof(ParallelCrossPartitionQueryPipelineStage)}"))); } if (parallelContinuationTokenListRaw.Count == 0) { return(TryCatch <CrossFeedRangeState <QueryState> > .FromException( new MalformedContinuationTokenException( $"Invalid format for continuation token {continuationToken} for {nameof(ParallelCrossPartitionQueryPipelineStage)}"))); } List <ParallelContinuationToken> parallelContinuationTokens = new List <ParallelContinuationToken>(); foreach (CosmosElement parallelContinuationTokenRaw in parallelContinuationTokenListRaw) { TryCatch <ParallelContinuationToken> tryCreateParallelContinuationToken = ParallelContinuationToken.TryCreateFromCosmosElement(parallelContinuationTokenRaw); if (tryCreateParallelContinuationToken.Failed) { return(TryCatch <CrossFeedRangeState <QueryState> > .FromException( tryCreateParallelContinuationToken.Exception)); } parallelContinuationTokens.Add(tryCreateParallelContinuationToken.Result); } TryCatch <PartitionMapping <ParallelContinuationToken> > partitionMappingMonad = PartitionMapper.MonadicGetPartitionMapping( ranges, parallelContinuationTokens); if (partitionMappingMonad.Failed) { return(TryCatch <CrossFeedRangeState <QueryState> > .FromException( partitionMappingMonad.Exception)); } PartitionMapping <ParallelContinuationToken> partitionMapping = partitionMappingMonad.Result; List <FeedRangeState <QueryState> > feedRangeStates = new List <FeedRangeState <QueryState> >(); List <IReadOnlyDictionary <FeedRangeEpk, ParallelContinuationToken> > rangesToInitialize = new List <IReadOnlyDictionary <FeedRangeEpk, ParallelContinuationToken> >() { // Skip all the partitions left of the target range, since they have already been drained fully. partitionMapping.TargetMapping, partitionMapping.MappingRightOfTarget, }; foreach (IReadOnlyDictionary <FeedRangeEpk, ParallelContinuationToken> rangeToInitalize in rangesToInitialize) { foreach (KeyValuePair <FeedRangeEpk, ParallelContinuationToken> kvp in rangeToInitalize) { FeedRangeState <QueryState> feedRangeState = new FeedRangeState <QueryState>(kvp.Key, kvp.Value?.Token != null ? new QueryState(CosmosString.Create(kvp.Value.Token)) : null); feedRangeStates.Add(feedRangeState); } } CrossFeedRangeState <QueryState> crossPartitionState = new CrossFeedRangeState <QueryState>(feedRangeStates.ToArray()); return(TryCatch <CrossFeedRangeState <QueryState> > .FromResult(crossPartitionState)); }
public async ValueTask <bool> MoveNextAsync(ITrace trace) { this.cancellationToken.ThrowIfCancellationRequested(); if (trace == null) { throw new ArgumentNullException(nameof(trace)); } if (!await this.crossPartitionRangePageAsyncEnumerator.MoveNextAsync(trace)) { this.Current = default; return(false); } TryCatch <CrossFeedRangePage <QueryPage, QueryState> > currentCrossPartitionPage = this.crossPartitionRangePageAsyncEnumerator.Current; if (currentCrossPartitionPage.Failed) { this.Current = TryCatch <QueryPage> .FromException(currentCrossPartitionPage.Exception); return(true); } CrossFeedRangePage <QueryPage, QueryState> crossPartitionPageResult = currentCrossPartitionPage.Result; QueryPage backendQueryPage = crossPartitionPageResult.Page; CrossFeedRangeState <QueryState> crossPartitionState = crossPartitionPageResult.State; QueryState queryState; if (crossPartitionState == null) { queryState = null; } else { // left most and any non null continuations IOrderedEnumerable <FeedRangeState <QueryState> > feedRangeStates = crossPartitionState .Value .ToArray() .OrderBy(tuple => ((FeedRangeEpk)tuple.FeedRange).Range.Min); List <ParallelContinuationToken> activeParallelContinuationTokens = new List <ParallelContinuationToken>(); { FeedRangeState <QueryState> firstState = feedRangeStates.First(); ParallelContinuationToken firstParallelContinuationToken = new ParallelContinuationToken( token: firstState.State != null ? ((CosmosString)firstState.State.Value).Value : null, range: ((FeedRangeEpk)firstState.FeedRange).Range); activeParallelContinuationTokens.Add(firstParallelContinuationToken); } foreach (FeedRangeState <QueryState> feedRangeState in feedRangeStates.Skip(1)) { this.cancellationToken.ThrowIfCancellationRequested(); if (feedRangeState.State != null) { ParallelContinuationToken parallelContinuationToken = new ParallelContinuationToken( token: feedRangeState.State != null ? ((CosmosString)feedRangeState.State.Value).Value : null, range: ((FeedRangeEpk)feedRangeState.FeedRange).Range); activeParallelContinuationTokens.Add(parallelContinuationToken); } } IEnumerable <CosmosElement> cosmosElementContinuationTokens = activeParallelContinuationTokens .Select(token => ParallelContinuationToken.ToCosmosElement(token)); CosmosArray cosmosElementParallelContinuationTokens = CosmosArray.Create(cosmosElementContinuationTokens); queryState = new QueryState(cosmosElementParallelContinuationTokens); } QueryPage crossPartitionQueryPage = new QueryPage( backendQueryPage.Documents, backendQueryPage.RequestCharge, backendQueryPage.ActivityId, backendQueryPage.ResponseLengthInBytes, backendQueryPage.CosmosQueryExecutionInfo, backendQueryPage.DisallowContinuationTokenMessage, queryState); this.Current = TryCatch <QueryPage> .FromResult(crossPartitionQueryPage); return(true); }
public ReadFeedIteratorCore( IDocumentContainer documentContainer, string continuationToken, ReadFeedPaginationOptions readFeedPaginationOptions, QueryRequestOptions queryRequestOptions, CancellationToken cancellationToken) { this.queryRequestOptions = queryRequestOptions; readFeedPaginationOptions ??= ReadFeedPaginationOptions.Default; if (!string.IsNullOrEmpty(continuationToken)) { bool isNewArrayFormat = (continuationToken.Length >= 2) && (continuationToken[0] == '[') && (continuationToken[continuationToken.Length - 1] == ']'); if (!isNewArrayFormat) { // One of the two older formats if (!FeedRangeContinuation.TryParse(continuationToken, out FeedRangeContinuation feedRangeContinuation)) { // Backward compatible with old format feedRangeContinuation = new FeedRangeCompositeContinuation( containerRid: string.Empty, FeedRangeEpk.FullRange, new List <Documents.Routing.Range <string> >() { new Documents.Routing.Range <string>( Documents.Routing.PartitionKeyInternal.MinimumInclusiveEffectivePartitionKey, Documents.Routing.PartitionKeyInternal.MaximumExclusiveEffectivePartitionKey, isMinInclusive: true, isMaxInclusive: false) }, continuationToken); } // need to massage it a little List <CosmosElement> feedRangeStates = new List <CosmosElement>(); string oldContinuationFormat = feedRangeContinuation.ToString(); if (feedRangeContinuation.FeedRange is FeedRangePartitionKey feedRangePartitionKey) { CosmosObject cosmosObject = CosmosObject.Parse(oldContinuationFormat); CosmosArray continuations = (CosmosArray)cosmosObject["Continuation"]; if (continuations.Count != 1) { throw new InvalidOperationException("Expected only one continuation for partition key queries"); } CosmosElement continuation = continuations[0]; CosmosObject continuationObject = (CosmosObject)continuation; CosmosElement token = continuationObject["token"]; ReadFeedState state; if (token is CosmosNull) { state = ReadFeedState.Beginning(); } else { CosmosString tokenAsString = (CosmosString)token; state = ReadFeedState.Continuation(CosmosElement.Parse(tokenAsString.Value)); } FeedRangeState <ReadFeedState> feedRangeState = new FeedRangeState <ReadFeedState>(feedRangePartitionKey, state); feedRangeStates.Add(ReadFeedFeedRangeStateSerializer.ToCosmosElement(feedRangeState)); } else { CosmosObject cosmosObject = CosmosObject.Parse(oldContinuationFormat); CosmosArray continuations = (CosmosArray)cosmosObject["Continuation"]; foreach (CosmosElement continuation in continuations) { CosmosObject continuationObject = (CosmosObject)continuation; CosmosObject rangeObject = (CosmosObject)continuationObject["range"]; string min = ((CosmosString)rangeObject["min"]).Value; string max = ((CosmosString)rangeObject["max"]).Value; CosmosElement token = continuationObject["token"]; FeedRangeInternal feedRange = new FeedRangeEpk(new Documents.Routing.Range <string>(min, max, isMinInclusive: true, isMaxInclusive: false)); ReadFeedState state; if (token is CosmosNull) { state = ReadFeedState.Beginning(); } else { CosmosString tokenAsString = (CosmosString)token; state = ReadFeedState.Continuation(CosmosElement.Parse(tokenAsString.Value)); } FeedRangeState <ReadFeedState> feedRangeState = new FeedRangeState <ReadFeedState>(feedRange, state); feedRangeStates.Add(ReadFeedFeedRangeStateSerializer.ToCosmosElement(feedRangeState)); } } CosmosArray cosmosArrayContinuationTokens = CosmosArray.Create(feedRangeStates); continuationToken = cosmosArrayContinuationTokens.ToString(); } } TryCatch <ReadFeedCrossFeedRangeState> monadicReadFeedState; if (continuationToken == null) { FeedRange feedRange; if ((this.queryRequestOptions != null) && this.queryRequestOptions.PartitionKey.HasValue) { feedRange = new FeedRangePartitionKey(this.queryRequestOptions.PartitionKey.Value); } else if ((this.queryRequestOptions != null) && (this.queryRequestOptions.FeedRange != null)) { feedRange = this.queryRequestOptions.FeedRange; } else { feedRange = FeedRangeEpk.FullRange; } monadicReadFeedState = TryCatch <ReadFeedCrossFeedRangeState> .FromResult(ReadFeedCrossFeedRangeState.CreateFromBeginning(feedRange)); } else { monadicReadFeedState = ReadFeedCrossFeedRangeState.Monadic.Parse(continuationToken); } if (monadicReadFeedState.Failed) { this.monadicEnumerator = TryCatch <CrossPartitionReadFeedAsyncEnumerator> .FromException(monadicReadFeedState.Exception); } else { this.monadicEnumerator = TryCatch <CrossPartitionReadFeedAsyncEnumerator> .FromResult( CrossPartitionReadFeedAsyncEnumerator.Create( documentContainer, new CrossFeedRangeState <ReadFeedState>(monadicReadFeedState.Result.FeedRangeStates), readFeedPaginationOptions, cancellationToken)); } this.hasMoreResults = true; }