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;
}