/// <summary> /// Creates a container as an asynchronous operation in the Azure Cosmos service. /// </summary> /// <param name="id">The cosmos container id</param> /// <param name="partitionKeyPath">The path to the partition key. Example: /location</param> /// <param name="throughput">(Optional) The throughput provisioned for a collection in measurement of Requests-per-Unit in the Azure Cosmos DB service.</param> /// <param name="requestOptions">(Optional) The options for the container request <see cref="CosmosRequestOptions"/></param> /// <param name="cancellationToken">(Optional) <see cref="CancellationToken"/> representing request cancellation.</param> /// <returns>A <see cref="Task"/> containing a <see cref="CosmosContainerResponse"/> which wraps a <see cref="CosmosContainerSettings"/> containing the read resource record.</returns> /// <exception cref="ArgumentNullException">If <paramref name="id"/> is not set.</exception> /// <exception cref="System.AggregateException">Represents a consolidation of failures that occurred during async processing. Look within InnerExceptions to find the actual exception(s).</exception> /// <exception cref="CosmosException">This exception can encapsulate many different types of errors. To determine the specific error always look at the StatusCode property. Some common codes you may get when creating a container are: /// <list type="table"> /// <listheader> /// <term>StatusCode</term><description>Reason for exception</description> /// </listheader> /// <item> /// <term>400</term><description>BadRequest - This means something was wrong with the request supplied. It is likely that an id was not supplied for the new container.</description> /// </item> /// <item> /// <term>403</term><description>Forbidden - This means you attempted to exceed your quota for containers. Contact support to have this quota increased.</description> /// </item> /// <item> /// <term>409</term><description>Conflict - This means a <see cref="CosmosContainerSettings"/> with an id matching the id you supplied already existed.</description> /// </item> /// </list> /// </exception> /// <example> /// /// <code language="c#"> /// <![CDATA[ /// CosmosContainerResponse response = this.cosmosDatabase.Containers.CreateContainerAsync(Guid.NewGuid().ToString()); /// ]]> /// </code> /// </example> public virtual Task <CosmosContainerResponse> CreateContainerAsync( string id, string partitionKeyPath, int?throughput = null, CosmosRequestOptions requestOptions = null, CancellationToken cancellationToken = default(CancellationToken)) { CosmosContainerSettings settings = new CosmosContainerSettings(id, partitionKeyPath); return(this.CreateContainerAsync( settings, throughput, requestOptions, cancellationToken)); }
private async Task <ShouldRetryResult> ShouldRetryInternalAsync( HttpStatusCode?statusCode, SubStatusCodes?subStatusCode, CancellationToken cancellationToken) { if (!statusCode.HasValue && (!subStatusCode.HasValue || subStatusCode.Value == SubStatusCodes.Unknown)) { return(null); } if (statusCode == HttpStatusCode.Gone && subStatusCode == SubStatusCodes.PartitionKeyRangeGone) { if (this.retried) { return(ShouldRetryResult.NoRetry()); } using (DocumentServiceRequest request = DocumentServiceRequest.Create( OperationType.Read, ResourceType.Collection, this.collectionLink, null, AuthorizationTokenType.PrimaryMasterKey)) { CosmosContainerSettings collection = await this.collectionCache.ResolveCollectionAsync(request, cancellationToken); CollectionRoutingMap routingMap = await this.partitionKeyRangeCache.TryLookupAsync(collection.ResourceId, null, request, cancellationToken); if (routingMap != null) { // Force refresh. await this.partitionKeyRangeCache.TryLookupAsync( collection.ResourceId, routingMap, request, cancellationToken); } } this.retried = true; return(ShouldRetryResult.RetryAfter(TimeSpan.FromSeconds(0))); } return(null); }
/// <summary> /// Used by typed API only. Exceptions are allowed. /// </summary> /// <param name="cancellationToken"></param> /// <returns>Returns the partition key path</returns> internal virtual async Task <string[]> GetPartitionKeyPathTokensAsync(CancellationToken cancellationToken = default(CancellationToken)) { CosmosContainerSettings containerSettings = await this.GetCachedContainerSettingsAsync(cancellationToken); if (containerSettings == null) { throw new ArgumentOutOfRangeException($"Container {this.LinkUri.ToString()} not found"); } if (containerSettings.PartitionKey?.Paths == null) { throw new ArgumentOutOfRangeException($"Partition key not defined for container {this.LinkUri.ToString()}"); } return(containerSettings.PartitionKeyPathTokens); }
public override Task <CosmosResponseMessage> ReplaceStreamAsync( CosmosContainerSettings containerSettings, ContainerRequestOptions requestOptions = null, CancellationToken cancellationToken = default(CancellationToken)) { if (containerSettings == null) { throw new ArgumentNullException(nameof(containerSettings)); } this.ClientContext.ValidateResource(containerSettings.Id); return(this.ReplaceStreamInternalAsync( streamPayload: this.ClientContext.SettingsSerializer.ToStream(containerSettings), requestOptions: requestOptions, cancellationToken: cancellationToken)); }
/// <summary> /// Creates a container as an asynchronous operation in the Azure Cosmos service. /// </summary> /// <param name="containerSettings">The <see cref="CosmosContainerSettings"/> object.</param> /// <param name="throughput">(Optional) The throughput provisioned for a collection in measurement of Requests-per-Unit in the Azure Cosmos DB service.</param> /// <param name="requestOptions">(Optional) The options for the container request <see cref="CosmosRequestOptions"/></param> /// <param name="cancellationToken">(Optional) <see cref="CancellationToken"/> representing request cancellation.</param> /// <returns>A <see cref="Task"/> containing a <see cref="CosmosContainerResponse"/> which wraps a <see cref="CosmosContainerSettings"/> containing the read resource record.</returns> /// <exception cref="ArgumentNullException">If either <paramref name="containerSettings"/> is not set.</exception> /// <exception cref="System.AggregateException">Represents a consolidation of failures that occurred during async processing. Look within InnerExceptions to find the actual exception(s).</exception> /// <exception cref="CosmosException">This exception can encapsulate many different types of errors. To determine the specific error always look at the StatusCode property. Some common codes you may get when creating a container are: /// <list type="table"> /// <listheader> /// <term>StatusCode</term><description>Reason for exception</description> /// </listheader> /// <item> /// <term>400</term><description>BadRequest - This means something was wrong with the request supplied. It is likely that an id was not supplied for the new container.</description> /// </item> /// <item> /// <term>403</term><description>Forbidden - This means you attempted to exceed your quota for containers. Contact support to have this quota increased.</description> /// </item> /// <item> /// <term>409</term><description>Conflict - This means a <see cref="CosmosContainerSettings"/> with an id matching the id you supplied already existed.</description> /// </item> /// </list> /// </exception> /// <example> /// /// <code language="c#"> /// <![CDATA[ /// CosmosContainerSettings settings = new CosmosContainerSettings() /// { /// Id = Guid.NewGuid().ToString(), /// IndexingPolicy = new IndexingPolicy() /// { /// Automatic = false, /// IndexingMode = IndexingMode.Lazy, /// }; /// }; /// /// CosmosContainerResponse response = this.cosmosDatabase.Containers.CreateContainerAsync(settings); /// ]]> /// </code> /// </example> public virtual Task <CosmosContainerResponse> CreateContainerAsync( CosmosContainerSettings containerSettings, int?throughput = null, CosmosRequestOptions requestOptions = null, CancellationToken cancellationToken = default(CancellationToken)) { return(CosmosContainers.ProcessCollectionCreateAsync( containerSettings: containerSettings, database: this.database, throughput: throughput, responseCreator: response => this.database.Client.ResponseFactory.CreateContainerResponse( response, new CosmosContainer(this.database, containerSettings.Id)), requestOptions: requestOptions, cancellationToken: cancellationToken)); }
private Task <CosmosContainerResponse> ProcessAsync( CosmosContainerSettings containerSettings, OperationType operationType, CosmosContainerRequestOptions requestOptions = null, CancellationToken cancellationToken = default(CancellationToken)) { return(ExecUtils.ProcessResourceOperationAsync <CosmosContainerResponse>( this.Client, this.LinkUri, ResourceType.Collection, operationType, requestOptions, containerSettings?.GetResourceStream(), response => this.Client.ResponseFactory.CreateContainerResponse(response, this), cancellationToken)); }
private static void AssertSerializedPayloads(CosmosContainerSettings settings, DocumentCollection documentCollection) { var jsonSettings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }; // HAKC: Work-around till BE fixes defautls settings.ValidateRequiredProperties(); string containerSerialized = JsonConvert.SerializeObject(settings, jsonSettings); string collectionSerialized = CosmosContainerSettingsTests.SerializeDocumentCollection(documentCollection); JObject containerJObject = JObject.Parse(containerSerialized); JObject collectionJObject = JObject.Parse(collectionSerialized); Assert.AreEqual(JsonConvert.SerializeObject(OrderProeprties(collectionJObject)), JsonConvert.SerializeObject(OrderProeprties(containerJObject))); }
public void DefaultIncludesPopulated() { CosmosContainerSettings containerSettings = new CosmosContainerSettings("TestContainer", "/partitionKey"); Assert.IsNotNull(containerSettings.IndexingPolicy); containerSettings.IndexingPolicy = new IndexingPolicy(); Assert.AreEqual(0, containerSettings.IndexingPolicy.IncludedPaths.Count); // HAKC: Work-around till BE fixes defautls containerSettings.ValidateRequiredProperties(); Assert.AreEqual(1, containerSettings.IndexingPolicy.IncludedPaths.Count); IncludedPath defaultEntry = containerSettings.IndexingPolicy.IncludedPaths[0]; Assert.AreEqual(IndexingPolicy.DefaultPath, defaultEntry.Path); Assert.AreEqual(0, defaultEntry.Indexes.Count); }
public void DefaultSameAsDocumentCollection() { CosmosContainerSettings containerSettings = new CosmosContainerSettings("TestContainer", "/partitionKey"); DocumentCollection dc = new DocumentCollection() { Id = "TestContainer", PartitionKey = new PartitionKeyDefinition() { Paths = new Collection <string>() { "/partitionKey" } } }; CosmosContainerSettingsTests.AssertSerializedPayloads(containerSettings, dc); }
public override Task <CosmosResponseMessage> CreateContainerAsStreamAsync( CosmosContainerSettings containerSettings, int?throughput = null, RequestOptions requestOptions = null, CancellationToken cancellationToken = default(CancellationToken)) { if (containerSettings == null) { throw new ArgumentNullException(nameof(containerSettings)); } this.ValidateContainerSettings(containerSettings); Stream streamPayload = this.ClientContext.SettingsSerializer.ToStream(containerSettings); return(this.CreateContainerAsStreamInternalAsync(streamPayload, throughput, requestOptions, cancellationToken)); }
public override Task <ContainerResponse> CreateContainerIfNotExistsAsync( string id, string partitionKeyPath, int?throughput = null, RequestOptions requestOptions = null, CancellationToken cancellationToken = default(CancellationToken)) { if (string.IsNullOrEmpty(id)) { throw new ArgumentNullException(nameof(id)); } if (string.IsNullOrEmpty(partitionKeyPath)) { throw new ArgumentNullException(nameof(partitionKeyPath)); } CosmosContainerSettings settings = new CosmosContainerSettings(id, partitionKeyPath); return(this.CreateContainerIfNotExistsAsync(settings, throughput, requestOptions, cancellationToken)); }
public override Task <ContainerResponse> CreateContainerAsync( CosmosContainerSettings containerSettings, int?requestUnitsPerSecond = null, RequestOptions requestOptions = null, CancellationToken cancellationToken = default(CancellationToken)) { if (containerSettings == null) { throw new ArgumentNullException(nameof(containerSettings)); } this.ValidateContainerSettings(containerSettings); Task <CosmosResponseMessage> response = this.CreateContainerStreamInternalAsync( streamPayload: this.ClientContext.SettingsSerializer.ToStream(containerSettings), requestUnitsPerSecond: requestUnitsPerSecond, requestOptions: requestOptions, cancellationToken: cancellationToken); return(this.ClientContext.ResponseFactory.CreateContainerResponseAsync(this.GetContainer(containerSettings.Id), response)); }
public override Task <CosmosContainerResponse> CreateContainerAsync( CosmosContainerSettings containerSettings, int?throughput = null, CosmosRequestOptions requestOptions = null, CancellationToken cancellationToken = default(CancellationToken)) { if (containerSettings == null) { throw new ArgumentNullException(nameof(containerSettings)); } this.ValidateContainerSettings(containerSettings); Task <CosmosResponseMessage> response = this.CreateContainerStreamAsync( streamPayload: CosmosResource.ToStream(containerSettings), throughput: throughput, requestOptions: requestOptions, cancellationToken: cancellationToken); return(this.client.ResponseFactory.CreateContainerResponse(this[containerSettings.Id], response)); }
private async Task <ResolutionResult> TryResolveServerPartitionByPartitionKeyRangeIdAsync( DocumentServiceRequest request, CosmosContainerSettings collection, CollectionRoutingMap routingMap, bool collectionCacheIsUpToDate, bool routingMapCacheIsUpToDate, bool forceRefreshPartitionAddresses, CancellationToken cancellationToken) { PartitionKeyRange partitionKeyRange = routingMap.TryGetRangeByPartitionKeyRangeId(request.PartitionKeyRangeIdentity.PartitionKeyRangeId); if (partitionKeyRange == null) { DefaultTrace.TraceInformation("Cannot resolve range '{0}'", request.PartitionKeyRangeIdentity.ToHeader()); return(this.HandleRangeAddressResolutionFailure(request, collectionCacheIsUpToDate, routingMapCacheIsUpToDate, routingMap)); } ServiceIdentity identity = routingMap.TryGetInfoByPartitionKeyRangeId(request.PartitionKeyRangeIdentity.PartitionKeyRangeId); PartitionAddressInformation addresses = await this.addressCache.TryGetAddressesAsync( request, new PartitionKeyRangeIdentity(collection.ResourceId, request.PartitionKeyRangeIdentity.PartitionKeyRangeId), identity, forceRefreshPartitionAddresses, cancellationToken); if (addresses == null) { DefaultTrace.TraceInformation("Cannot resolve addresses for range '{0}'", request.PartitionKeyRangeIdentity.ToHeader()); return(this.HandleRangeAddressResolutionFailure(request, collectionCacheIsUpToDate, routingMapCacheIsUpToDate, routingMap)); } return(new ResolutionResult(partitionKeyRange, addresses, identity)); }
private async Task <ShouldRetryResult> ShouldRetryInternalAsync( HttpStatusCode?statusCode, SubStatusCodes?subStatusCode, Task <ShouldRetryResult> chainedRetryTask, CancellationToken cancellationToken) { ShouldRetryResult shouldRetry = await chainedRetryTask; if (this.request == null) { DefaultTrace.TraceCritical("Cannot apply RenameCollectionAwareClientRetryPolicy as OnBeforeSendRequest has not been called and there is no DocumentServiceRequest context. Status Code {0} Sub Status Code {1}", statusCode.HasValue? statusCode.Value : 0, subStatusCode.HasValue ? subStatusCode.Value : SubStatusCodes.Unknown); return(shouldRetry); } if (!shouldRetry.ShouldRetry && !this.hasTriggered && statusCode.HasValue && subStatusCode.HasValue) { if (this.request.IsNameBased && statusCode.Value == HttpStatusCode.NotFound && subStatusCode.Value == SubStatusCodes.ReadSessionNotAvailable) { // Clear the session token, because the collection name might be reused. DefaultTrace.TraceWarning("Clear the the token for named base request {0}", request.ResourceAddress); this.sessionContainer.ClearTokenByCollectionFullname(request.ResourceAddress); this.hasTriggered = true; string oldCollectionRid = request.RequestContext.ResolvedCollectionRid; request.ForceNameCacheRefresh = true; request.RequestContext.ResolvedCollectionRid = null; try { CosmosContainerSettings collectionInfo = await this.collectionCache.ResolveCollectionAsync(request, cancellationToken); if (collectionInfo == null) { DefaultTrace.TraceCritical("Can't recover from session unavailable exception because resolving collection name {0} returned null", request.ResourceAddress); } else if (!string.IsNullOrEmpty(oldCollectionRid) && !string.IsNullOrEmpty(collectionInfo.ResourceId)) { if (!oldCollectionRid.Equals(collectionInfo.ResourceId)) { return(ShouldRetryResult.RetryAfter(TimeSpan.Zero)); } } } catch (Exception e) { // When ResolveCollectionAsync throws an exception ignore it because it's an attempt to recover an existing // error. When the recovery fails we return ShouldRetryResult.NoRetry and propaganate the original exception to the client DefaultTrace.TraceCritical("Can't recover from session unavailable exception because resolving collection name {0} failed with {1}", request.ResourceAddress, e.ToString()); } } } return(shouldRetry); }
/// <summary> /// Creates a container as an asynchronous operation in the Azure Cosmos service. /// </summary> /// <param name="containerSettings">The <see cref="CosmosContainerSettings"/> object.</param> /// <param name="requestUnitsPerSecond">(Optional) The throughput provisioned for a container in measurement of Requests Units per second in the Azure Cosmos DB service.</param> /// <param name="requestOptions">(Optional) The options for the container request <see cref="RequestOptions"/></param> /// <param name="cancellationToken">(Optional) <see cref="CancellationToken"/> representing request cancellation.</param> /// <returns>A <see cref="Task"/> containing a <see cref="ContainerResponse"/> which wraps a <see cref="CosmosContainerSettings"/> containing the read resource record.</returns> /// <exception cref="ArgumentNullException">If either <paramref name="containerSettings"/> is not set.</exception> /// <exception cref="System.AggregateException">Represents a consolidation of failures that occurred during async processing. Look within InnerExceptions to find the actual exception(s).</exception> /// <exception cref="CosmosException">This exception can encapsulate many different types of errors. To determine the specific error always look at the StatusCode property. Some common codes you may get when creating a container are: /// <list type="table"> /// <listheader> /// <term>StatusCode</term><description>Reason for exception</description> /// </listheader> /// <item> /// <term>400</term><description>BadRequest - This means something was wrong with the request supplied. It is likely that an id was not supplied for the new container.</description> /// </item> /// <item> /// <term>403</term><description>Forbidden - This means you attempted to exceed your quota for containers. Contact support to have this quota increased.</description> /// </item> /// <item> /// <term>409</term><description>Conflict - This means a <see cref="CosmosContainerSettings"/> with an id matching the id you supplied already existed.</description> /// </item> /// </list> /// </exception> /// <example> /// /// <code language="c#"> /// <![CDATA[ /// CosmosContainerSettings settings = new CosmosContainerSettings() /// { /// Id = Guid.NewGuid().ToString(), /// IndexingPolicy = new IndexingPolicy() /// { /// Automatic = false, /// IndexingMode = IndexingMode.Lazy, /// }; /// }; /// /// ContainerResponse response = this.cosmosDatabase.CreateContainerAsync(settings); /// ]]> /// </code> /// </example> /// <seealso cref="DefineContainer(string, string)"/> /// <remarks> /// <seealso href="https://docs.microsoft.com/azure/cosmos-db/request-units"/> for details on provision throughput. /// </remarks> public abstract Task <ContainerResponse> CreateContainerAsync( CosmosContainerSettings containerSettings, int?requestUnitsPerSecond = null, RequestOptions requestOptions = null, CancellationToken cancellationToken = default(CancellationToken));
/// <summary> /// Replace a <see cref="CosmosContainerSettings"/> from the Azure Cosmos service as an asynchronous operation. /// </summary> /// <param name="containerSettings">The <see cref="CosmosContainerSettings"/> object.</param> /// <param name="requestOptions">(Optional) The options for the container request <see cref="CosmosRequestOptions"/></param> /// <param name="cancellationToken">(Optional) <see cref="CancellationToken"/> representing request cancellation.</param> /// <returns> /// A <see cref="Task"/> containing a <see cref="CosmosContainerResponse"/> which wraps a <see cref="CosmosContainerSettings"/> containing the replace resource record. /// </returns> /// <exception cref="CosmosException">This exception can encapsulate many different types of errors. To determine the specific error always look at the StatusCode property. Some common codes you may get when creating a Document are: /// <list type="table"> /// <listheader> /// <term>StatusCode</term><description>Reason for exception</description> /// </listheader> /// <item> /// <term>404</term><description>NotFound - This means the resource you tried to read did not exist.</description> /// </item> /// <item> /// <term>429</term><description>TooManyRequests - This means you have exceeded the number of request units per second. Consult the DocumentClientException.RetryAfter value to see how long you should wait before retrying this operation.</description> /// </item> /// </list> /// </exception> /// <example> /// Update the cosmosContainer to disable automatic indexing /// <code language="c#"> /// <![CDATA[ /// ContainerSettings setting = containerReadResponse; /// setting.IndexingPolicy.Automatic = false; /// CosmosContainerResponse response = cosmosContainer.ReplaceAsync(setting); /// ContainerSettings settings = response; /// ]]> /// </code> /// </example> public abstract Task <CosmosContainerResponse> ReplaceAsync( CosmosContainerSettings containerSettings, CosmosContainerRequestOptions requestOptions = null, CancellationToken cancellationToken = default(CancellationToken));
/// <summary> /// Resolves the endpoint of the partition for the given request /// </summary> /// <param name="request">Request for which the partition endpoint resolution is to be performed</param> /// <param name="forceRefreshPartitionAddresses">Force refresh the partition's endpoint</param> /// <param name="cancellationToken">Cancellation token</param> /// <returns>An instance of <see cref="ResolutionResult"/>.</returns> private async Task <ResolutionResult> ResolveAddressesAndIdentityAsync( DocumentServiceRequest request, bool forceRefreshPartitionAddresses, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); if (request.ServiceIdentity != null) { // 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) { 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; CosmosContainerSettings collection = await this.collectionCache.ResolveCollectionAsync(request, cancellationToken); CollectionRoutingMap routingMap = await this.collectionRoutingMapCache.TryLookupAsync( collection.ResourceId, null, request, cancellationToken); if (routingMap != null && request.ForceCollectionRoutingMapRefresh) { DefaultTrace.TraceInformation( "AddressResolver.ResolveAddressesAndIdentityAsync ForceCollectionRoutingMapRefresh collection.ResourceId = {0}", collection.ResourceId); routingMap = await this.collectionRoutingMapCache.TryLookupAsync(collection.ResourceId, routingMap, request, cancellationToken); } if (request.ForcePartitionKeyRangeRefresh) { collectionRoutingMapCacheIsUptoDate = true; request.ForcePartitionKeyRangeRefresh = false; if (routingMap != null) { routingMap = await this.collectionRoutingMapCache.TryLookupAsync(collection.ResourceId, routingMap, request, cancellationToken); } } 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); routingMap = await this.collectionRoutingMapCache.TryLookupAsync( collection.ResourceId, previousValue : null, request : request, cancellationToken : cancellationToken); } 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; collectionCacheIsUptoDate = true; collection = await this.collectionCache.ResolveCollectionAsync(request, cancellationToken); 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( collection.ResourceId, previousValue : null, request : request, cancellationToken : cancellationToken); } } if (!collectionRoutingMapCacheIsUptoDate) { collectionRoutingMapCacheIsUptoDate = true; routingMap = await this.collectionRoutingMapCache.TryLookupAsync( collection.ResourceId, previousValue : routingMap, request : request, cancellationToken : cancellationToken); } 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); }
private async Task <ResolutionResult> TryResolveServerPartitionAsync( DocumentServiceRequest request, CosmosContainerSettings collection, CollectionRoutingMap routingMap, bool collectionCacheIsUptodate, bool collectionRoutingMapCacheIsUptodate, bool forceRefreshPartitionAddresses, CancellationToken cancellationToken) { // Check if this request partitionkeyrange-aware routing logic. We cannot retry here in this case // and need to bubble up errors. if (request.PartitionKeyRangeIdentity != null) { return(await this.TryResolveServerPartitionByPartitionKeyRangeIdAsync( request, collection, routingMap, collectionCacheIsUptodate, collectionRoutingMapCacheIsUptodate, forceRefreshPartitionAddresses, cancellationToken)); } if (!request.ResourceType.IsPartitioned() && !(request.ResourceType == ResourceType.StoredProcedure && request.OperationType == OperationType.ExecuteJavaScript) && // Collection head is sent internally for strong consistency given routing hints from original requst, which is for partitioned resource. !(request.ResourceType == ResourceType.Collection && request.OperationType == OperationType.Head)) { DefaultTrace.TraceCritical( "Shouldn't come here for non partitioned resources. resourceType : {0}, operationtype:{1}, resourceaddress:{2}", request.ResourceType, request.OperationType, request.ResourceAddress); throw new InternalServerErrorException(RMResources.InternalServerError) { ResourceAddress = request.ResourceAddress }; } PartitionKeyRange range; string partitionKeyString = request.Headers[HttpConstants.HttpHeaders.PartitionKey]; object effectivePartitionKeyStringObject = null; if (partitionKeyString != null) { range = this.TryResolveServerPartitionByPartitionKey( request, partitionKeyString, collectionCacheIsUptodate, collection, routingMap); } else if (request.Properties != null && request.Properties.TryGetValue( WFConstants.BackendHeaders.EffectivePartitionKeyString, out effectivePartitionKeyStringObject)) { // Allow EPK only for partitioned collection (excluding migrated fixed collections) if (!collection.HasPartitionKey || collection.PartitionKey.IsSystemKey) { throw new ArgumentOutOfRangeException(nameof(collection)); } string effectivePartitionKeyString = effectivePartitionKeyStringObject as string; if (string.IsNullOrEmpty(effectivePartitionKeyString)) { throw new ArgumentOutOfRangeException(nameof(effectivePartitionKeyString)); } range = routingMap.GetRangeByEffectivePartitionKey(effectivePartitionKeyString); } else { range = this.TryResolveSinglePartitionCollection(request, routingMap, collectionCacheIsUptodate); } if (range == null) { // Collection cache or routing map cache is potentially outdated. Return null - // upper logic will refresh cache and retry. return(null); } ServiceIdentity serviceIdentity = routingMap.TryGetInfoByPartitionKeyRangeId(range.Id); PartitionAddressInformation addresses = await this.addressCache.TryGetAddressesAsync( request, new PartitionKeyRangeIdentity(collection.ResourceId, range.Id), serviceIdentity, forceRefreshPartitionAddresses, cancellationToken); if (addresses == null) { DefaultTrace.TraceVerbose( "Could not resolve addresses for identity {0}/{1}. Potentially collection cache or routing map cache is outdated. Return null - upper logic will refresh and retry. ", new PartitionKeyRangeIdentity(collection.ResourceId, range.Id), serviceIdentity); return(null); } return(new ResolutionResult(range, addresses, serviceIdentity)); }
private PartitionKeyRange TryResolveServerPartitionByPartitionKey( DocumentServiceRequest request, string partitionKeyString, bool collectionCacheUptoDate, CosmosContainerSettings collection, CollectionRoutingMap routingMap) { if (request == null) { throw new ArgumentNullException("request"); } if (partitionKeyString == null) { throw new ArgumentNullException("partitionKeyString"); } if (collection == null) { throw new ArgumentNullException("collection"); } if (routingMap == null) { throw new ArgumentNullException("routingMap"); } PartitionKeyInternal partitionKey; try { partitionKey = PartitionKeyInternal.FromJsonString(partitionKeyString); } catch (JsonException ex) { throw new BadRequestException( string.Format(CultureInfo.InvariantCulture, RMResources.InvalidPartitionKey, partitionKeyString), ex) { ResourceAddress = request.ResourceAddress }; } if (partitionKey == null) { throw new InternalServerErrorException(string.Format(CultureInfo.InvariantCulture, "partition key is null '{0}'", partitionKeyString)); } if (partitionKey.Equals(PartitionKeyInternal.Empty) || partitionKey.Components.Count == collection.PartitionKey.Paths.Count) { // Although we can compute effective partition key here, in general case this Gateway can have outdated // partition key definition cached - like if collection with same name but with Range partitioning is created. // In this case server will not pass x-ms-documentdb-collection-rid check and will return back InvalidPartitionException. // Gateway will refresh its cache and retry. string effectivePartitionKey = partitionKey.GetEffectivePartitionKeyString(collection.PartitionKey); // There should be exactly one range which contains a partition key. Always. return(routingMap.GetRangeByEffectivePartitionKey(effectivePartitionKey)); } if (collectionCacheUptoDate) { BadRequestException badRequestException = new BadRequestException(RMResources.PartitionKeyMismatch) { ResourceAddress = request.ResourceAddress }; badRequestException.Headers[WFConstants.BackendHeaders.SubStatus] = ((uint)SubStatusCodes.PartitionKeyMismatch).ToString(CultureInfo.InvariantCulture); throw badRequestException; } // Partition key supplied has different number paths than locally cached partition key definition. // Three things can happen: // 1. User supplied wrong partition key. // 2. Client SDK has outdated partition key definition cache and extracted wrong value from the document. // 3. Gateway's cache is outdated. // // What we will do is append x-ms-documentdb-collection-rid header and forward it to random collection partition. // * If collection rid matches, server will send back 400.1001, because it also will not be able to compute // effective partition key. Gateway will forward this status code to client - client will handle it. // * If collection rid doesn't match, server will send back InvalidPartiitonException and Gateway will // refresh name routing cache - this will refresh partition key definition as well, and retry. DefaultTrace.TraceInformation( "Cannot compute effective partition key. Definition has '{0}' paths, values supplied has '{1}' paths. Will refresh cache and retry.", collection.PartitionKey.Paths.Count, partitionKey.Components.Count); return(null); }
internal void ValidateContainerSettings(CosmosContainerSettings containerSettings) { containerSettings.ValidateRequiredProperties(); this.client.DocumentClient.ValidateResource(containerSettings.Id); }
/// <summary> /// Instantiates a new instance of the <see cref="PartitionKeyInternal"/> object. /// </summary> /// <remarks> /// The function selects the right partition key constant for inserting documents that don't have /// a value for partition key. The constant selection is based on whether the collection is migrated /// or user partitioned /// </remarks> internal async Task <PartitionKeyInternal> GetNonePartitionKeyValue(CancellationToken cancellationToken = default(CancellationToken)) { CosmosContainerSettings containerSettings = await this.GetCachedContainerSettingsAsync(cancellationToken); return(containerSettings.GetNoneValue()); }
/// <summary> /// Creates a container as an asynchronous operation in the Azure Cosmos service. /// </summary> /// <param name="containerSettings">The <see cref="CosmosContainerSettings"/> object.</param> /// <param name="throughput">(Optional) The throughput provisioned for a collection in measurement of Requests-per-Unit in the Azure Cosmos DB service.</param> /// <param name="requestOptions">(Optional) The options for the container request <see cref="RequestOptions"/></param> /// <param name="cancellationToken">(Optional) <see cref="CancellationToken"/> representing request cancellation.</param> /// <returns>A <see cref="Task"/> containing a <see cref="CosmosResponseMessage"/> containing the created resource record.</returns> /// <seealso cref="DefineContainer(string, string)"/> public abstract Task <CosmosResponseMessage> CreateContainerAsStreamAsync( CosmosContainerSettings containerSettings, int?throughput = null, RequestOptions requestOptions = null, CancellationToken cancellationToken = default(CancellationToken));
/// <summary> /// Check if a container exists, and if it doesn't, create it. /// This will make a read operation, and if the container is not found it will do a create operation. /// </summary> /// <param name="containerSettings">The <see cref="CosmosContainerSettings"/> object.</param> /// <param name="throughput">(Optional) The throughput provisioned for a collection in measurement of Requests-per-Unit in the Azure Cosmos DB service.</param> /// <param name="requestOptions">(Optional) The options for the container request <see cref="RequestOptions"/></param> /// <param name="cancellationToken">(Optional) <see cref="CancellationToken"/> representing request cancellation.</param> /// <returns>A <see cref="Task"/> containing a <see cref="ContainerResponse"/> which wraps a <see cref="CosmosContainerSettings"/> containing the read resource record.</returns> /// <exception cref="ArgumentNullException">If either <paramref name="containerSettings"/> is not set.</exception> /// <exception cref="System.AggregateException">Represents a consolidation of failures that occurred during async processing. Look within InnerExceptions to find the actual exception(s).</exception> /// <exception cref="CosmosException">This exception can encapsulate many different types of errors. To determine the specific error always look at the StatusCode property. Some common codes you may get when creating a container are: /// <list type="table"> /// <listheader> /// <term>StatusCode</term><description>Reason for exception</description> /// </listheader> /// <item> /// <term>400</term><description>BadRequest - This means something was wrong with the request supplied. It is likely that an id was not supplied for the new container.</description> /// </item> /// <item> /// <term>403</term><description>Forbidden - This means you attempted to exceed your quota for containers. Contact support to have this quota increased.</description> /// </item> /// <item> /// <term>409</term><description>Conflict - This means a <see cref="CosmosContainerSettings"/> with an id matching the id you supplied already existed.</description> /// </item> /// </list> /// </exception> /// <example> /// /// <code language="c#"> /// <![CDATA[ /// CosmosContainerSettings settings = new CosmosContainerSettings() /// { /// Id = Guid.NewGuid().ToString(), /// IndexingPolicy = new IndexingPolicy() /// { /// Automatic = false, /// IndexingMode = IndexingMode.Lazy, /// }; /// }; /// /// ContainerResponse response = this.cosmosDatabase.Containers.CreateContainerIfNotExistsAsync(settings); /// ]]> /// </code> /// </example> public abstract Task <ContainerResponse> CreateContainerIfNotExistsAsync( CosmosContainerSettings containerSettings, int?throughput = null, RequestOptions requestOptions = null, CancellationToken cancellationToken = default(CancellationToken));