Beispiel #1
0
        private async Task <FeedResource <Address> > GetMasterAddressesViaGatewayAsync(
            DocumentServiceRequest request,
            ResourceType resourceType,
            string resourceAddress,
            string entryUrl,
            bool forceRefresh,
            bool useMasterCollectionResolver)
        {
            INameValueCollection addressQuery = new DictionaryNameValueCollection(StringComparer.Ordinal);

            addressQuery.Add(HttpConstants.QueryStrings.Url, HttpUtility.UrlEncode(entryUrl));

            INameValueCollection headers = new DictionaryNameValueCollection(StringComparer.Ordinal);

            if (forceRefresh)
            {
                headers.Set(HttpConstants.HttpHeaders.ForceRefresh, bool.TrueString);
            }

            if (useMasterCollectionResolver)
            {
                headers.Set(HttpConstants.HttpHeaders.UseMasterCollectionResolver, bool.TrueString);
            }

            if (request.ForceCollectionRoutingMapRefresh)
            {
                headers.Set(HttpConstants.HttpHeaders.ForceCollectionRoutingMapRefresh, bool.TrueString);
            }

            addressQuery.Add(HttpConstants.QueryStrings.Filter, this.protocolFilter);

            string resourceTypeToSign = PathsHelper.GetResourcePath(resourceType);

            headers.Set(HttpConstants.HttpHeaders.XDate, DateTime.UtcNow.ToString("r", CultureInfo.InvariantCulture));
            string token = this.tokenProvider.GetUserAuthorizationToken(
                resourceAddress,
                resourceTypeToSign,
                HttpConstants.HttpMethods.Get,
                headers,
                AuthorizationTokenType.PrimaryMasterKey,
                payload: out _);

            headers.Set(HttpConstants.HttpHeaders.Authorization, token);

            Uri targetEndpoint = UrlUtility.SetQuery(this.addressEndpoint, UrlUtility.CreateQuery(addressQuery));

            string identifier = GatewayAddressCache.LogAddressResolutionStart(request, targetEndpoint);

            using (HttpResponseMessage httpResponseMessage = await this.httpClient.GetAsync(targetEndpoint, headers))
            {
                using (DocumentServiceResponse documentServiceResponse =
                           await ClientExtensions.ParseResponseAsync(httpResponseMessage))
                {
                    GatewayAddressCache.LogAddressResolutionEnd(request, identifier);
                    return(documentServiceResponse.GetResource <FeedResource <Address> >());
                }
            }
        }
        private async Task <Tuple <PartitionKeyRangeIdentity, PartitionAddressInformation> > ResolveMasterAsync(DocumentServiceRequest request, bool forceRefresh)
        {
            Tuple <PartitionKeyRangeIdentity, PartitionAddressInformation> masterAddressAndRange = this.masterPartitionAddressCache;

            int targetReplicaSetSize = this.serviceConfigReader.SystemReplicationPolicy.MaxReplicaSetSize;

            forceRefresh = forceRefresh ||
                           (masterAddressAndRange != null &&
                            masterAddressAndRange.Item2.AllAddresses.Count() < targetReplicaSetSize &&
                            DateTime.UtcNow.Subtract(this.suboptimalMasterPartitionTimestamp) > TimeSpan.FromSeconds(this.suboptimalPartitionForceRefreshIntervalInSeconds));

            if (forceRefresh || request.ForceCollectionRoutingMapRefresh || this.masterPartitionAddressCache == null)
            {
                string entryUrl = PathsHelper.GeneratePath(
                    ResourceType.Database,
                    string.Empty,
                    true);

                try
                {
                    using (DocumentServiceResponse response = await this.GetMasterAddressesViaGatewayAsync(
                               request,
                               ResourceType.Database,
                               null,
                               entryUrl,
                               forceRefresh,
                               false))
                    {
                        FeedResource <Address> masterAddresses = response.GetResource <FeedResource <Address> >();

                        bool inNetworkRequest = this.IsInNetworkRequest(response);

                        masterAddressAndRange                   = this.ToPartitionAddressAndRange(string.Empty, masterAddresses.ToList(), inNetworkRequest);
                        this.masterPartitionAddressCache        = masterAddressAndRange;
                        this.suboptimalMasterPartitionTimestamp = DateTime.MaxValue;
                    }
                }
                catch (Exception)
                {
                    this.suboptimalMasterPartitionTimestamp = DateTime.MaxValue;
                    throw;
                }
            }

            if (masterAddressAndRange.Item2.AllAddresses.Count() < targetReplicaSetSize && this.suboptimalMasterPartitionTimestamp.Equals(DateTime.MaxValue))
            {
                this.suboptimalMasterPartitionTimestamp = DateTime.UtcNow;
            }

            return(masterAddressAndRange);
        }
        private async Task <PartitionAddressInformation> GetAddressesForRangeIdAsync(
            DocumentServiceRequest request,
            string collectionRid,
            string partitionKeyRangeId,
            bool forceRefresh)
        {
            using (DocumentServiceResponse response =
                       await this.GetServerAddressesViaGatewayAsync(request, collectionRid, new[] { partitionKeyRangeId }, forceRefresh))
            {
                FeedResource <Address> addressFeed = response.GetResource <FeedResource <Address> >();

                bool inNetworkRequest = this.IsInNetworkRequest(response);

                IEnumerable <Tuple <PartitionKeyRangeIdentity, PartitionAddressInformation> > addressInfos =
                    addressFeed.Where(addressInfo => ProtocolFromString(addressInfo.Protocol) == this.protocol)
                    .GroupBy(address => address.PartitionKeyRangeId, StringComparer.Ordinal)
                    .Select(group => this.ToPartitionAddressAndRange(collectionRid, @group.ToList(), inNetworkRequest));

                Tuple <PartitionKeyRangeIdentity, PartitionAddressInformation> result =
                    addressInfos.SingleOrDefault(
                        addressInfo => StringComparer.Ordinal.Equals(addressInfo.Item1.PartitionKeyRangeId, partitionKeyRangeId));

                if (result == null)
                {
                    string errorMessage = string.Format(
                        CultureInfo.InvariantCulture,
                        RMResources.PartitionKeyRangeNotFound,
                        partitionKeyRangeId,
                        collectionRid);

                    throw new PartitionKeyRangeGoneException(errorMessage)
                          {
                              ResourceAddress = collectionRid
                          };
                }

                return(result.Item2);
            }
        }
Beispiel #4
0
        private async Task <CollectionRoutingMap> GetRoutingMapForCollectionAsync(
            string collectionRid,
            CollectionRoutingMap previousRoutingMap,
            ITrace trace,
            IClientSideRequestStatistics clientSideRequestStatistics,
            CancellationToken cancellationToken)
        {
            List <PartitionKeyRange> ranges  = new List <PartitionKeyRange>();
            string changeFeedNextIfNoneMatch = previousRoutingMap == null ? null : previousRoutingMap.ChangeFeedNextIfNoneMatch;

            HttpStatusCode lastStatusCode = HttpStatusCode.OK;

            do
            {
                INameValueCollection headers = new StoreRequestNameValueCollection();

                headers.Set(HttpConstants.HttpHeaders.PageSize, PageSizeString);
                headers.Set(HttpConstants.HttpHeaders.A_IM, HttpConstants.A_IMHeaderValues.IncrementalFeed);
                if (changeFeedNextIfNoneMatch != null)
                {
                    headers.Set(HttpConstants.HttpHeaders.IfNoneMatch, changeFeedNextIfNoneMatch);
                }

                RetryOptions retryOptions = new RetryOptions();
                using (DocumentServiceResponse response = await BackoffRetryUtility <DocumentServiceResponse> .ExecuteAsync(
                           () => this.ExecutePartitionKeyRangeReadChangeFeedAsync(collectionRid, headers, trace, clientSideRequestStatistics),
                           new ResourceThrottleRetryPolicy(retryOptions.MaxRetryAttemptsOnThrottledRequests, retryOptions.MaxRetryWaitTimeInSeconds),
                           cancellationToken))
                {
                    lastStatusCode            = response.StatusCode;
                    changeFeedNextIfNoneMatch = response.Headers[HttpConstants.HttpHeaders.ETag];

                    FeedResource <PartitionKeyRange> feedResource = response.GetResource <FeedResource <PartitionKeyRange> >();
                    if (feedResource != null)
                    {
                        ranges.AddRange(feedResource);
                    }
                }
            }while (lastStatusCode != HttpStatusCode.NotModified);

            IEnumerable <Tuple <PartitionKeyRange, ServiceIdentity> > tuples = ranges.Select(range => Tuple.Create(range, (ServiceIdentity)null));

            CollectionRoutingMap routingMap;

            if (previousRoutingMap == null)
            {
                // Splits could have happened during change feed query and we might have a mix of gone and new ranges.
                HashSet <string> goneRanges = new HashSet <string>(ranges.SelectMany(range => range.Parents ?? Enumerable.Empty <string>()));
                routingMap = CollectionRoutingMap.TryCreateCompleteRoutingMap(
                    tuples.Where(tuple => !goneRanges.Contains(tuple.Item1.Id)),
                    string.Empty,
                    changeFeedNextIfNoneMatch);
            }
            else
            {
                routingMap = previousRoutingMap.TryCombine(tuples, changeFeedNextIfNoneMatch);
            }

            if (routingMap == null)
            {
                // Range information either doesn't exist or is not complete.
                throw new NotFoundException($"{DateTime.UtcNow.ToString("o", CultureInfo.InvariantCulture)}: GetRoutingMapForCollectionAsync(collectionRid: {collectionRid}), Range information either doesn't exist or is not complete.");
            }

            return(routingMap);
        }
Beispiel #5
0
        private async Task <FeedResource <Address> > GetServerAddressesViaGatewayAsync(
            DocumentServiceRequest request,
            string collectionRid,
            IEnumerable <string> partitionKeyRangeIds,
            bool forceRefresh)
        {
            string entryUrl = PathsHelper.GeneratePath(ResourceType.Document, collectionRid, true);

            INameValueCollection addressQuery = new DictionaryNameValueCollection();

            addressQuery.Add(HttpConstants.QueryStrings.Url, HttpUtility.UrlEncode(entryUrl));

            INameValueCollection headers = new DictionaryNameValueCollection();

            if (forceRefresh)
            {
                headers.Set(HttpConstants.HttpHeaders.ForceRefresh, bool.TrueString);
            }

            if (request.ForceCollectionRoutingMapRefresh)
            {
                headers.Set(HttpConstants.HttpHeaders.ForceCollectionRoutingMapRefresh, bool.TrueString);
            }

            addressQuery.Add(HttpConstants.QueryStrings.Filter, this.protocolFilter);
            addressQuery.Add(HttpConstants.QueryStrings.PartitionKeyRangeIds, string.Join(",", partitionKeyRangeIds));

            string resourceTypeToSign = PathsHelper.GetResourcePath(ResourceType.Document);

            headers.Set(HttpConstants.HttpHeaders.XDate, DateTime.UtcNow.ToString("r", CultureInfo.InvariantCulture));
            string token = null;

            try
            {
                token = this.tokenProvider.GetUserAuthorizationToken(
                    collectionRid,
                    resourceTypeToSign,
                    HttpConstants.HttpMethods.Get,
                    headers,
                    AuthorizationTokenType.PrimaryMasterKey);
            }
            catch (UnauthorizedException)
            {
            }

            if (token == null && request.IsNameBased)
            {
                // User doesn't have rid based resource token. Maybe he has name based.
                string collectionAltLink = PathsHelper.GetCollectionPath(request.ResourceAddress);
                token = this.tokenProvider.GetUserAuthorizationToken(
                    collectionAltLink,
                    resourceTypeToSign,
                    HttpConstants.HttpMethods.Get,
                    headers,
                    AuthorizationTokenType.PrimaryMasterKey);
            }

            headers.Set(HttpConstants.HttpHeaders.Authorization, token);

            Uri targetEndpoint = UrlUtility.SetQuery(this.addressEndpoint, UrlUtility.CreateQuery(addressQuery));

            string identifier = GatewayAddressCache.LogAddressResolutionStart(request, targetEndpoint);

            using (HttpResponseMessage httpResponseMessage = await this.httpClient.GetAsync(targetEndpoint, headers))
            {
                using (DocumentServiceResponse documentServiceResponse =
                           await ClientExtensions.ParseResponseAsync(httpResponseMessage))
                {
                    GatewayAddressCache.LogAddressResolutionEnd(request, identifier);

                    return(documentServiceResponse.GetResource <FeedResource <Address> >());
                }
            }
        }