private async Task ResolveAddressesAndIdentityAsync()

in Microsoft.Azure.Cosmos/src/Routing/AddressResolver.cs [166:389]


        private async Task<ResolutionResult> ResolveAddressesAndIdentityAsync(
            DocumentServiceRequest request,
            bool forceRefreshPartitionAddresses,
            CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();

            if (request.ServiceIdentity != null)
            {
                if (request.ServiceIdentity.IsMasterService &&
                    request.ForceMasterRefresh &&
                    this.masterServiceIdentityProvider != null)
                {
                    await this.masterServiceIdentityProvider.RefreshAsync(request.ServiceIdentity, cancellationToken);

                    ServiceIdentity newMasterServiceIdentity = this.masterServiceIdentityProvider.MasterServiceIdentity;

                    bool masterServiceIdentityChanged = newMasterServiceIdentity != null &&
                        !newMasterServiceIdentity.Equals(request.ServiceIdentity);

                    DefaultTrace.TraceInformation(
                        "Refreshed master service identity. masterServiceIdentityChanged = {0}, " +
                        "previousRequestServiceIdentity = {1}, newMasterServiceIdentity = {2}",
                        masterServiceIdentityChanged,
                        request.ServiceIdentity,
                        newMasterServiceIdentity);

                    if (masterServiceIdentityChanged)
                    {
                        request.RouteTo(newMasterServiceIdentity);
                    }
                }

                // In this case we don't populate request.RequestContext.ResolvedPartitionKeyRangeId,
                // which is needed for session token.
                // The assumption is that:
                //     1. Master requests never use session consistency.
                //     2. Service requests (like collection create etc.) don't use session consistency.
                //     3. Requests which target specific partition of an existing collection will use x-ms-documentdb-partitionkeyrangeid header
                //        to send request to specific partition and will not set request.ServiceIdentity
                ServiceIdentity identity = request.ServiceIdentity;
                PartitionAddressInformation addresses = await this.addressCache.TryGetAddressesAsync(request, null, identity, forceRefreshPartitionAddresses, cancellationToken);

                if (addresses == null && identity.IsMasterService && this.masterServiceIdentityProvider != null)
                {
                    DefaultTrace.TraceWarning("Could not get addresses for MasterServiceIdentity {0}. will refresh masterServiceIdentity and retry", identity);
                    await this.masterServiceIdentityProvider.RefreshAsync(identity, cancellationToken);
                    identity = this.masterServiceIdentityProvider.MasterServiceIdentity;
                    addresses = await this.addressCache.TryGetAddressesAsync(request, null, identity, forceRefreshPartitionAddresses, cancellationToken);
                }

                if (addresses == null)
                {
                    DefaultTrace.TraceInformation("Could not get addresses for explicitly specified ServiceIdentity {0}", identity);
                    throw new NotFoundException() { ResourceAddress = request.ResourceAddress };
                }

                return new ResolutionResult(addresses, identity);
            }

            if (ReplicatedResourceClient.IsReadingFromMaster(request.ResourceType, request.OperationType) && request.PartitionKeyRangeIdentity == null)
            {
                DefaultTrace.TraceInformation("Resolving Master service address, forceMasterRefresh: {0}, currentMaster: {1}",
                    request.ForceMasterRefresh,
                    this.masterServiceIdentityProvider?.MasterServiceIdentity);

                // Client implementation, GlobalAddressResolver passes in a null IMasterServiceIdentityProvider, because it doesn't actually use the serviceIdentity
                // in the addressCache.TryGetAddresses method. In GatewayAddressCache.cs, the master address is resolved by making a call to Gateway AddressFeed,
                // not using the serviceIdentity that is passed in
                if (request.ForceMasterRefresh && this.masterServiceIdentityProvider != null)
                {
                    ServiceIdentity previousMasterService = this.masterServiceIdentityProvider.MasterServiceIdentity;
                    await this.masterServiceIdentityProvider.RefreshAsync(previousMasterService, cancellationToken);
                }
                ServiceIdentity serviceIdentity = this.masterServiceIdentityProvider?.MasterServiceIdentity;
                PartitionKeyRangeIdentity partitionKeyRangeIdentity = this.masterPartitionKeyRangeIdentity;
                PartitionAddressInformation addresses = await this.addressCache.TryGetAddressesAsync(
                    request,
                    partitionKeyRangeIdentity,
                    serviceIdentity,
                    forceRefreshPartitionAddresses,
                    cancellationToken);
                if (addresses == null)
                {
                    // This shouldn't really happen.
                    DefaultTrace.TraceCritical("Could not get addresses for master partition {0}", serviceIdentity);
                    throw new NotFoundException() { ResourceAddress = request.ResourceAddress };
                }

                PartitionKeyRange partitionKeyRange = new PartitionKeyRange { Id = PartitionKeyRange.MasterPartitionKeyRangeId };
                return new ResolutionResult(partitionKeyRange, addresses, serviceIdentity);
            }

            bool collectionCacheIsUptoDate = !request.IsNameBased ||
                (request.PartitionKeyRangeIdentity != null && request.PartitionKeyRangeIdentity.CollectionRid != null);

            bool collectionRoutingMapCacheIsUptoDate = false;

            ContainerProperties collection = await this.collectionCache.ResolveCollectionAsync(request, cancellationToken, NoOpTrace.Singleton);
            CollectionRoutingMap routingMap = await this.collectionRoutingMapCache.TryLookupAsync(
                collectionRid: collection.ResourceId,
                previousValue: null,
                request: request,
                trace: NoOpTrace.Singleton);

            if (routingMap != null && request.ForceCollectionRoutingMapRefresh)
            {
                DefaultTrace.TraceInformation(
                    "AddressResolver.ResolveAddressesAndIdentityAsync ForceCollectionRoutingMapRefresh collection.ResourceId = {0}",
                    collection.ResourceId);

                routingMap = await this.collectionRoutingMapCache.TryLookupAsync(
                    collectionRid: collection.ResourceId,
                    previousValue: routingMap,
                    request: request,
                    trace: NoOpTrace.Singleton);
            }

            if (request.ForcePartitionKeyRangeRefresh)
            {
                collectionRoutingMapCacheIsUptoDate = true;
                request.ForcePartitionKeyRangeRefresh = false;
                if (routingMap != null)
                {
                    routingMap = await this.collectionRoutingMapCache.TryLookupAsync(
                        collectionRid: collection.ResourceId,
                        previousValue: routingMap,
                        request: request,
                        trace: NoOpTrace.Singleton);
                }
            }

            if (routingMap == null && !collectionCacheIsUptoDate)
            {
                // Routing map was not found by resolved collection rid. Maybe collection rid is outdated.
                // Refresh collection cache and reresolve routing map.
                request.ForceNameCacheRefresh = true;
                collectionCacheIsUptoDate = true;
                collectionRoutingMapCacheIsUptoDate = false;
                collection = await this.collectionCache.ResolveCollectionAsync(request, cancellationToken, NoOpTrace.Singleton);
                routingMap = await this.collectionRoutingMapCache.TryLookupAsync(
                        collectionRid: collection.ResourceId,
                        previousValue: null,
                        request: request,
                        trace: NoOpTrace.Singleton);
            }

            AddressResolver.EnsureRoutingMapPresent(request, routingMap, collection);

            // At this point we have both collection and routingMap.
            ResolutionResult result = await this.TryResolveServerPartitionAsync(
                request,
                collection,
                routingMap,
                collectionCacheIsUptoDate,
                collectionRoutingMapCacheIsUptodate: collectionRoutingMapCacheIsUptoDate,
                forceRefreshPartitionAddresses: forceRefreshPartitionAddresses,
                cancellationToken: cancellationToken);

            if (result == null)
            {
                // Couldn't resolve server partition or its addresses.
                // Either collection cache is outdated or routing map cache is outdated.
                if (!collectionCacheIsUptoDate)
                {
                    request.ForceNameCacheRefresh = true;
                    collection = await this.collectionCache.ResolveCollectionAsync(request, cancellationToken, NoOpTrace.Singleton);
                    if (collection.ResourceId != routingMap.CollectionUniqueId)
                    {
                        // Collection cache was stale. We resolved to new Rid. routing map cache is potentially stale
                        // for this new collection rid. Mark it as such.
                        collectionRoutingMapCacheIsUptoDate = false;
                        routingMap = await this.collectionRoutingMapCache.TryLookupAsync(
                            collectionRid: collection.ResourceId,
                            previousValue: null,
                            request: request,
                            trace: NoOpTrace.Singleton);
                    }
                }

                if (!collectionRoutingMapCacheIsUptoDate)
                {
                    routingMap = await this.collectionRoutingMapCache.TryLookupAsync(
                        collection.ResourceId,
                        previousValue: routingMap,
                        request: request,
                        trace: NoOpTrace.Singleton);
                }

                AddressResolver.EnsureRoutingMapPresent(request, routingMap, collection);

                result = await this.TryResolveServerPartitionAsync(
                    request,
                    collection,
                    routingMap,
                    collectionCacheIsUptodate: true,
                    collectionRoutingMapCacheIsUptodate: true,
                    forceRefreshPartitionAddresses: forceRefreshPartitionAddresses,
                    cancellationToken: cancellationToken);
            }

            if (result == null)
            {
                DefaultTrace.TraceInformation("Couldn't route partitionkeyrange-oblivious request after retry/cache refresh. Collection doesn't exist.");

                // At this point collection cache and routing map caches are refreshed.
                // The only reason we will get here is if collection doesn't exist.
                // Case when partitionkeyrange doesn't exist is handled in the corresponding method.
                throw new NotFoundException() { ResourceAddress = request.ResourceAddress };
            }

            if (request.IsNameBased)
            {
                // Append collection rid.
                // If we resolved collection rid incorrectly because of outdated cache, this can lead 
                // to incorrect routing decisions. But backend will validate collection rid and throw
                // InvalidPartitionException if we reach wrong collection.
                // Also this header will be used by backend to inject collection rid into metrics for
                // throttled requests.
                request.Headers[WFConstants.BackendHeaders.CollectionRid] = collection.ResourceId;
            }

            return result;
        }